Dalam artikel sebelumnyaMenggunakan kaedah getBoundingClientRect untuk melaksanakan komponen melekit mudah memperkenalkan pelaksanaan mudah komponen melekit Selepas memikirkannya sejak dua hari lalu, saya mendapati terdapat lebih banyak pelaksanaan yang disediakan kali terakhir. Kelemahannya ialah kesan yang dicapai pada laman web lain juga agak berbeza apabila menyahbaikan kaedah yang disediakan kali terakhir adalah tidak baik Berdasarkan perkara di atas, artikel ini menyediakan versi yang lebih baik daripada komponen lengkap, saya harap anda berminat untuk membaca.
1. Masalah dengan versi lama
Terdapat pelbagai masalah dalam pelaksanaan komponen melekit sebelumnya:
Pertama, dari segi kesan melekit, sebelum dan selepas elemen melekit dibetulkan, apa yang tidak akan berubah adalah kedudukan berbanding sebelah kiri pelayar dan lebar keseluruhan elemen melekit kedudukan relatif kepada bahagian atas atau bawah pelayar dan ketinggian elemen melekit Dalam pelaksanaan yang disediakan di atas, dua nilai yang berubah-ubah dianggap sebagai nilai tetap. Mengapakah nilai atas atau nilai bawah sentiasa 0 apabila ia ditetapkan? Sudah tentu, ia boleh menjadi selain daripada 0, seperti atas: 20px, bawah: 15px Dalam sesetengah adegan, menambahkan beberapa ofset sedemikian akan menjadikan kesan melekit kelihatan lebih baik, seperti contoh komponen imbuhan yang digunakan dalam dokumentasi rasmi bootstrap (ini. komponen Fungsi ini serupa dengan komponen melekit yang dilaksanakan dalam artikel ini):
Ia menetapkan kedudukan berbanding bahagian atas penyemak imbas ke atas: 20px apabila ditetapkan. Perkara yang sama berlaku untuk ketinggian unsur melekit Untuk memaparkan kesan yang kelihatan lebih baik apabila ditetapkan, ia juga merupakan keperluan yang sangat biasa untuk melaraskan ketinggian Garisan atau padding-top dan atribut lain yang berkaitan dengan ketinggian. halaman Tmall Huabei ini, kandungan Blok ini menggunakan komponen melekit:
Sebelum ditetapkan, ketinggian unsur melekit ialah:
Selepas ditetapkan, ketinggian unsur melekit ialah:
Kedua, apabila membatalkan penetapan, mengambil elemen melekit yang ditetapkan di bahagian atas sebagai contoh, pelaksanaan yang disediakan di atas adalah untuk membatalkan terus kedudukan elemen melekit apabila jarak antara elemen sasaran dan bahagian atas pelayar adalah kurang daripada stickyHeight: atribut tetap, elemen melekat segera dipulihkan kepada aliran dokumen biasa dan kesannya ialah:
Ia hilang serta-merta pada titik kritikal, tetapi kesan Tmall Huabei tidak seperti ini:
Ia tidak hilang serta-merta apabila ia mencapai titik kritikal, tetapi melaraskan semula nilai teratas unsur melekit supaya ia boleh menatal ke atas bersama-sama dengan kandungan utama halaman web bersama-sama dengan bar skrol:
Dari sudut pengalaman, adalah jelas bahawa kesan Tmall Huabei adalah lebih baik Dari sudut fungsi, pelaksanaan yang disediakan di atas mempunyai kelemahan yang membawa maut: apabila ketinggian elemen melekit sangat besar, ia. melebihi keupayaan penyemak imbas Apabila melihat ketinggian kawasan, akan ada pepijat yang tidak kira bagaimana anda menatal, anda tidak boleh menyemak imbas semua kandungan elemen melekit Jika anda berminat, anda boleh mencuba kod yang dilaksanakan kali terakhir bar sisi blog anda. Saya mencuba dan menemui masalah ini, jadi saya ingin memperbaiki komponen melekit: (
Ketiga, pelaksanaan terakhir masih mempunyai beberapa kelemahan:
1) documentElement.clientHeight tidak dicache, menyebabkan ia diperoleh semula setiap kali titik kritikal dinilai:
2) Nilai lalai selang panggil balik skrol adalah terlalu besar dan harus ditetapkan lebih kecil kali ini 5 dan bootstrap menggunakan 1. Hanya dengan cara ini kesannya boleh dijamin lancar
4) Apabila elemen melekit dibetulkan dan tidak dibetulkan, fungsi panggil balik harus disediakan supaya komponen lain boleh melakukan perkara pada titik penting apabila ia bergantung pada komponen ini.
2. Bagaimana untuk menambah baik
Pilihan komponen telah ditakrifkan semula:
var DEFAULTS = { target: '', //target元素的jq选择器 type: 'top', //固定的位置,top | bottom,默认为top,表示固定在顶部 wait: 5, //scroll事件回调的间隔 stickyOffset: 0, //固定时距离浏览器可视区顶部或底部的偏移,用来设置top跟bottom属性的值,默认为0 isFixedWidth: true, //sticky元素宽度是否固定,默认为true,如果是自适应的宽度,需设置为false getStickyWidth: undefined, //用来获取sticky元素宽度的回调,在不传该参数的情况下,stickyWidth将设置为sticky元素的offsetWidth unStickyDistance: undefined, //该参数决定sticky元素何时进入dynamicSticky状态 onSticky: undefined, ///sticky元素固定时的回调 onUnSticky: undefined ///sticky元素取消固定时的回调 };
Yang dalam huruf tebal adalah baharu atau diubah suai Ketinggian asal telah dialih keluar dan digantikan dengan unStickyDistance. Apabila membetulkan, kedudukan relatif kepada bahagian atas atau bawah penyemak imbas ditentukan dengan stickyOffset, supaya tidak perlu menulis nilai atribut atas atau bawah dalam css .sticky--in-top atau .sticky--in -bawah. Jika isFixedWidth palsu, panggilan balik untuk menyegarkan lebar elemen melekit semasa mengubah saiz akan ditambah:
!opts.isFixedWidth && $win.resize(throttle(function () { setStickyWidth(); $elem.hasClass(className) && $elem.css('width', stickyWidth); sticky(); }, opts.wait));
Berbanding dengan kali terakhir, masalah dalam pelaksanaan ini ialah pemprosesan logik apabila membatalkan penetapan Kali terakhir, elemen melekit hanya mempunyai dua keadaan, melekit atau tidak melekit staticSticky dan dynamicSticky yang pertama Ia mewakili keadaan melekit di mana nilai atas atau bawah kekal tidak berubah Untuk menyelesaikan masalah ini dengan lebih jelas, pertimbangan asal ialah Titik kritikal dan kod yang melakukan pemprosesan yang berbeza pada titik kritikal yang berbeza distruktur semula kepada yang berikut:
setSticky = function () { !$elem.hasClass(className) && $elem.addClass(className).css('width', stickyWidth) && (typeof opts.onSticky == 'function' && opts.onSticky($elem, $target)); return true; }, states = { staticSticky: function () { setSticky() && $elem.css(opts.type, opts.stickyOffset); }, dynamicSticky: function (rect) { setSticky() && $elem.css(opts.type, rules[opts.type].getDynamicOffset(rect)); }, unSticky: function () { $elem.hasClass(className) && $elem.removeClass(className).css('width', '').css(opts.type, '') && (typeof opts.onUnSticky == 'function' && opts.onUnSticky($elem, $target)); } }, rules = { top: { getState: function (rect) { if (rect.top < 0 && (rect.bottom - unStickyDistance) > 0) return 'staticSticky'; else if ((rect.bottom - unStickyDistance) <= 0 && rect.bottom > 0) return 'dynamicSticky'; else return 'unSticky'; }, getDynamicOffset: function (rect) { return -(unStickyDistance - rect.bottom); } }, bottom: { getState: function (rect) { if (rect.bottom > docClientHeight && (rect.top + unStickyDistance) < docClientHeight) return 'staticSticky'; else if ((rect.top + unStickyDistance) >= docClientHeight && rect.top < docClientHeight) return 'dynamicSticky'; else return 'unSticky'; }, getDynamicOffset: function (rect) { return -(unStickyDistance + rect.top - docClientHeight); } } } $win.scroll(throttle(sticky, opts.wait)); function sticky() { var rect = $target[0].getBoundingClientRect(), curState = rules[opts.type].getState(rect); states[curState](rect); }
Terdapat sedikit idea model keadaan di dalamnya, tetapi ia lebih ringkas. Apabila saya menulis kod ini, saya benar-benar mahu menggunakan mesin negeri yang telah saya pelajari sebelum ini tunggu suatu hari nanti. Cuba lagi apabila anda ingin berlatih mesin negeri.
Pelaksanaan keseluruhan adalah seperti berikut:
var Sticky = (function ($) { function throttle(func, wait) { var timer = null; return function () { var self = this, args = arguments; if (timer) clearTimeout(timer); timer = setTimeout(function () { return typeof func === 'function' && func.apply(self, args); }, wait); } } var DEFAULTS = { target: '', //target元素的jq选择器 type: 'top', //固定的位置,top | bottom,默认为top,表示固定在顶部 wait: 5, //scroll事件回调的间隔 stickyOffset: 0, //固定时距离浏览器可视区顶部或底部的偏移,用来设置top跟bottom属性的值,默认为0 isFixedWidth: true, //sticky元素宽度是否固定,默认为true,如果是自适应的宽度,需设置为false getStickyWidth: undefined, //用来获取sticky元素宽度的回调,在不传该参数的情况下,stickyWidth将设置为sticky元素的offsetWidth unStickyDistance: undefined, //该参数决定sticky元素何时进入dynamicSticky状态 onSticky: undefined, ///sticky元素固定时的回调 onUnSticky: undefined ///sticky元素取消固定时的回调 }; return function (elem, opts) { var $elem = $(elem); opts = $.extend({}, DEFAULTS, opts || {}, $elem.data() || {}); var $target = $(opts.target); if (!$elem.length || !$target.length) return; var stickyWidth, setStickyWidth = function () { stickyWidth = typeof opts.getStickyWidth === 'function' && opts.getStickyWidth($elem) || $elem[0].offsetWidth; }, docClientHeight = document.documentElement.clientHeight, unStickyDistance = opts.unStickyDistance || $elem[0].offsetHeight, setSticky = function () { !$elem.hasClass(className) && $elem.addClass(className).css('width', stickyWidth) && (typeof opts.onSticky == 'function' && opts.onSticky($elem, $target)); return true; }, states = { staticSticky: function () { setSticky() && $elem.css(opts.type, opts.stickyOffset); }, dynamicSticky: function (rect) { setSticky() && $elem.css(opts.type, rules[opts.type].getDynamicOffset(rect)); }, unSticky: function () { $elem.hasClass(className) && $elem.removeClass(className).css('width', '').css(opts.type, '') && (typeof opts.onUnSticky == 'function' && opts.onUnSticky($elem, $target)); } }, rules = { top: { getState: function (rect) { if (rect.top < 0 && (rect.bottom - unStickyDistance) > 0) return 'staticSticky'; else if ((rect.bottom - unStickyDistance) <= 0 && rect.bottom > 0) return 'dynamicSticky'; else return 'unSticky'; }, getDynamicOffset: function (rect) { return -(unStickyDistance - rect.bottom); } }, bottom: { getState: function (rect) { if (rect.bottom > docClientHeight && (rect.top + unStickyDistance) < docClientHeight) return 'staticSticky'; else if ((rect.top + unStickyDistance) >= docClientHeight && rect.top < docClientHeight) return 'dynamicSticky'; else return 'unSticky'; }, getDynamicOffset: function (rect) { return -(unStickyDistance + rect.top - docClientHeight); } } }, className = 'sticky--in-' + opts.type, $win = $(window); setStickyWidth(); $win.scroll(throttle(sticky, opts.wait)); !opts.isFixedWidth && $win.resize(throttle(function () { setStickyWidth(); $elem.hasClass(className) && $elem.css('width', stickyWidth); sticky(); }, opts.wait)); $win.resize(throttle(function () { docClientHeight = document.documentElement.clientHeight; }, opts.wait)); function sticky() { var rect = $target[0].getBoundingClientRect(), curState = rules[opts.type].getState(rect); states[curState](rect); } } })(jQuery);
Apa yang sukar difahami mungkin logik kaedah getState Beberapa idea dalam bahagian ini dijelaskan dengan lebih terperinci dalam blog sebelum ini.
3. Arahan aplikasi bar sisi blog
Mula-mula, anda mesti menampal pelaksanaan ini ke dalam medan teks html pengaki blog, dan kemudian tambah kod berikut untuk memulakan:
var timer = setInterval(function(){ if($('#blogCalendar').length && $('#profile_block').length && $('#sidebar_search').length) { new Sticky('#sideBar', { target: '#main', onSticky: function($elem, $target){ $target.css('min-height',$elem.outerHeight()); $elem.css('left', '65px'); }, onUnSticky: function($elem, $target){ $target.css('min-height',''); $elem.css('left', ''); } }); } },100);
Pemasa digunakan kerana kandungan bar sisi dimuatkan oleh ajax, dan adalah mustahil untuk menambah panggilan balik semasa permintaan ajax ini Anda hanya boleh menilai sama ada bar sisi dimuatkan melalui kandungan yang dikembalikan.
4. Ringkasan
Hujung minggu ini saya berfikir tentang bagaimana untuk menambah baik komponen melekit, saya menghabiskan hampir sepanjang hari menulis artikel ini masa. Terasa pelik, seperti ada yang kurang, tetapi ternyata kerana masih banyak perkara yang hilang. Pada masa ini, komponen ini hanya boleh mencapai kesan pembetulan dan pembetulan Untuk kerja sebenar, kesan tahap ini mungkin tidak mencukupi. Seterusnya Artikel ini akan memperkenalkan komponen melekit berdasarkan artikel ini, cara melaksanakan komponen navScrollSticky dan tabSticky, jadi nantikan.
Terima kasih kerana membaca :)
Penjelasan tambahan:
Dalam IE dan Firefox, apabila menyegarkan halaman, jika halaman menatal sebelum menyegarkan, operasi penyegaran akan menetapkan kedudukan tatal halaman ke kedudukan penyegaran, tetapi acara tatal tidak akan dicetuskan, jadi ia mesti dipanggil serta-merta selepas komponen dimulakan Fungsi melekit: