Im vorherigen ArtikelVerwendung der getBoundingClientRect-Methode zum Implementieren einer einfachen Sticky-Komponente habe ich eine einfache Implementierung der Sticky-Komponente vorgestellt. Nachdem ich in den letzten zwei Tagen darüber nachgedacht hatte, stellte ich fest, dass es weitere Implementierungen gibt Der Nachteil besteht darin, dass die auf anderen Websites erzielten Effekte beim letzten Mal nicht gut waren. Basierend auf dem oben Gesagten bietet dieser Artikel eine verbesserte Version der Sticky-Komponente vollständig, ich hoffe, Sie haben Interesse am Lesen.
1. Probleme mit alten Versionen
Es gibt mehrere Probleme bei der Implementierung der vorherigen Sticky-Komponente:
Was zunächst die Wirkung von Sticky betrifft, so ändert sich vor und nach der Fixierung des Sticky-Elements nicht die Position relativ zur linken Seite des Browsers und die Gesamtbreite des Sticky-Elements die Position relativ zur Ober- oder Unterseite des Browsers und die Höhe des Sticky-Elements. In der oben bereitgestellten Implementierung werden die beiden letztgenannten sich ändernden Werte als konstante Werte betrachtet. Warum ist der obere oder untere Wert immer 0, wenn er fest ist? Natürlich kann es auch ein anderer Wert als 0 sein, z. B. oben: 20 Pixel, unten: 15 Pixel. In einigen Szenen führt das Hinzufügen einiger solcher Offsets dazu, dass der Sticky-Effekt besser aussieht, z Komponente Die Funktion ähnelt der in diesem Artikel implementierten Sticky-Komponente):
Die Position relativ zum oberen Rand des Browsers wird auf „top: 20px“ festgelegt, wenn dies behoben ist. Das Gleiche gilt für die Höhe klebriger Elemente, um im fixierten Zustand einen besser aussehenden Effekt zu erzielen. Es ist auch eine sehr häufige Anforderung, die ursprüngliche Linienhöhe oder den oberen Rand sowie andere höhenbezogene Attribute anzupassen. Auf dieser Seite von Tmall Huabei verwendet dieser Blockinhalt die Sticky-Komponente:
Vor dem Fixieren beträgt die Höhe des Klebeelements:
Nach der Fixierung beträgt die Höhe des Klebeelements:
Zweitens besteht die oben bereitgestellte Implementierung beim Aufheben der Fixierung am Beispiel des oben fixierten Sticky-Elements darin, die Position des Sticky-Elements direkt abzubrechen, wenn der Abstand zwischen dem Zielelement und der Oberseite des Browsers gleich ist kleiner als stickyHeight: festes Attribut, das Sticky-Element wird sofort in den normalen Dokumentfluss zurückgesetzt und der Effekt ist:
Es verschwindet sofort, wenn es den kritischen Punkt erreicht, aber die Wirkung von Tmall Huabei ist nicht so:
Es verschwindet nicht sofort, wenn es den kritischen Punkt erreicht, sondern passt den oberen Wert des Sticky-Elements neu an, sodass es in Verbindung mit der Bildlaufleiste zusammen mit dem Hauptinhalt der Webseite nach oben scrollen kann:
Aus Erfahrungssicht ist es offensichtlich, dass die Wirkung von Tmall Huabei besser ist. Aus funktionaler Sicht weist die oben bereitgestellte Implementierung einen schwerwiegenden Mangel auf: Wenn die Höhe des klebrigen Elements sehr groß ist Übersteigt die Fähigkeiten des Browsers. Wenn Sie die Höhe des Bereichs anzeigen, tritt ein Fehler auf, der dazu führt, dass Sie nicht alle Inhalte der Sticky-Elemente durchsuchen können. Wenn Sie interessiert sind, können Sie den zuletzt implementierten Code ausprobieren die Seitenleiste Ihres Blogs. Ich habe dieses Problem ausprobiert und festgestellt, deshalb wollte ich die klebrige Komponente verbessern: (
Drittens weist die letzte Implementierung noch einige Mängel auf:
1) documentElement.clientHeight wird nicht zwischengespeichert, was dazu führt, dass es jedes Mal neu erfasst wird, wenn der kritische Punkt beurteilt wird:
2) Der Standardwert des Scroll-Callback-Intervalls ist zu groß und sollte kleiner eingestellt werden. Diesmal beträgt er 5 und Bootstrap verwendet 1. Nur so kann ein reibungsloser Effekt gewährleistet werden
4) Wenn das Sticky-Element fixiert und nicht fixiert ist, sollte eine Rückruffunktion bereitgestellt werden, damit andere Komponenten an wichtigen Punkten Dinge tun können, wenn sie von dieser Komponente abhängig sind.
2. Wie man sich verbessert
Die Komponentenoptionen wurden neu definiert:
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元素取消固定时的回调 };
Die fett gedruckten Werte sind neu oder geändert. Die ursprüngliche Höhe wurde entfernt und durch unStickyDistance ersetzt. Beim Fixieren wird die Position relativ zum oberen oder unteren Rand des Browsers mit stickyOffset angegeben, sodass der obere oder untere Attributwert nicht in das CSS von .sticky--in-top oder .sticky--in geschrieben werden muss -unten. Wenn isFixedWidth false ist, wird ein Rückruf zum Aktualisieren der Breite des Sticky-Elements während der Größenänderung hinzugefügt:
!opts.isFixedWidth && $win.resize(throttle(function () { setStickyWidth(); $elem.hasClass(className) && $elem.css('width', stickyWidth); sticky(); }, opts.wait));
Im Vergleich zum letzten Mal besteht das Problem bei dieser Implementierung in der logischen Verarbeitung beim Abbrechen der Fixierung. Beim letzten Mal gab es nur zwei Zustände: „klebrig“ und „nicht klebrig“. staticSticky und DynamicSticky. Ersteres stellt den klebrigen Zustand dar, in dem der obere oder untere Wert unverändert bleibt. Letzteres entspricht tatsächlich dem Bereich, in dem die Fixierung erfolgt Um dieses Problem klarer zu lösen, lautet das ursprüngliche Urteil: Die kritischen Punkte und der Code, der an verschiedenen kritischen Punkten unterschiedliche Verarbeitungen durchführt, werden wie folgt umstrukturiert:
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); }
Es enthält ein wenig die Idee des Staatsmodells, ist aber prägnanter. Als ich diesen Code schrieb, wollte ich unbedingt die Zustandsmaschine verwenden, die ich zuvor kennengelernt hatte, aber ich wollte nur verhindern, dass auf eine Klassenbibliothek verwiesen wird Versuchen Sie es eines Tages noch einmal, wenn Sie Zustandsautomaten üben möchten.
Die Gesamtimplementierung ist wie folgt:
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);
Was schwer zu verstehen ist, ist möglicherweise die Logik der getState-Methode. Einige der Ideen in diesem Teil wurden im vorherigen Blog ausführlicher erläutert.
3. Anleitung zur Blog-Seitenleiste
Zuerst müssen Sie diese Implementierung in das HTML-Textfeld für die Fußzeile der Blog-Einstellungen einfügen und dann den folgenden Code zur Initialisierung hinzufügen:
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);
Der Timer wird verwendet, da der Inhalt der Seitenleiste von Ajax geladen wird und es unmöglich ist, während dieser Ajax-Anfragen Rückrufe hinzuzufügen. Sie können nur anhand des zurückgegebenen Inhalts beurteilen, ob die Seitenleiste geladen wird.
4. Zusammenfassung
Dieses Wochenende habe ich darüber nachgedacht, wie ich die klebrige Komponente verbessern kann. Außerdem habe ich den größten Teil des Tages damit verbracht, diesen Artikel zu schreiben Es fühlte sich seltsam an, als würde etwas fehlen, aber es lag daran, dass noch so viele Dinge fehlten. Derzeit kann diese Komponente nur den Effekt des Fixierens und Aufhebens der Fixierung erzielen. Für die tatsächliche Arbeit reicht die Wirkung dieser Ebene möglicherweise nicht aus. Die im Internet üblichen Funktionen, die das Scrollen in der Navigation oder die Tab-Navigation unterstützen, sind ebenfalls sehr verbreitet. Weiter In diesem Artikel wird die Sticky-Komponente vorgestellt. Basierend auf diesem Artikel erfahren Sie, wie Sie die Komponenten navScrollSticky und tabSticky implementieren. Bleiben Sie also auf dem Laufenden.
Vielen Dank fürs Lesen :)
Zusätzliche Erklärung:
Wenn in IE und Firefox beim Aktualisieren der Seite die Seite vor dem Aktualisieren gescrollt wird, setzt der Aktualisierungsvorgang die Bildlaufposition der Seite auf die Aktualisierungsposition, das Bildlaufereignis wird jedoch nicht ausgelöst und muss daher unmittelbar danach aufgerufen werden Die Komponente wird initialisiert. Eine Sticky-Funktion: