Talking about the improvement of sticky component and realizing _javascript skill

Source: Internet
Author: User

In the last article, using the Getboundingclientrect method to implement a concise sticky component introduces a simple implementation of a sticky component, after two days of thinking, found that the last delivery of the implementation there are more deficiencies, In addition to the effect of the implementation of other sites in the cancellation of the fixed time there are some different, the last provision of the cancellation of fixed treatment is not good, this article on the basis of the above, to provide an improved version of the sticky components, more complete function, I hope you are interested in reading.

1. Old version of the problem

There are several issues in the implementation of the previous sticky component:

First, from the sticky effect, sticky elements in the fixed before and after, will not change relative to the left side of the browser and sticky elements of the overall width, may change is relative to the top or bottom of the browser position and the height of the sticky element, In the implementation provided above, the following two changing values are treated as immutable values. Why is the top value or bottom value 0 when fixed? Of course not 0, such as top:20px,bottom:15px, in some scenes, plus some of these offsets, sticky effect will look better, For example, the affix component instance used in bootstrap official documents (the functionality of this component is similar to the sticky component implemented in this article):

It will be fixed, relative to the position of the top of the browser set to top:20px. The height of the sticky elements is also, in order to display a more attractive effect in the fixed time, adjust the original line-height or Padding-top and other more highly related properties, is also a very common requirement, such as the day of the cat flower of this page, this piece of content is used sticky components:

Before fixing, the height of the sticky element is:

When fixed, the height of the sticky element is:

Second, in the case of cancellation, the sticky element is fixed at the top as an example, the implementation provided above is to cancel the position:fixed attribute of the sticky element directly when the target element is less than the top of the browser stickyheight. The sticky element is immediately restored to the normal document stream, and the effect is:

It disappears immediately at the critical point, and the effect of the day cat flower is not like this:

It does not disappear immediately at the critical point, but instead adjusts the top value of the sticky element, allowing it to scroll up with the main content of the page along with the scroll bar:

In terms of experience, it's obvious that the cat flower has a better effect, and functionally, the implementation provided above has a fatal disadvantage: when the sticky element is very large and exceeds the height of the browser's viewable area, it appears no matter how you scroll, Can not browse the full sticky elements of all the content of bugs, interested in the last implementation of the code in their blog sidebar to try. I have tried to find this problem, so I want to improve the sticky component: (
Third, there are several deficiencies in the last implementation:

1 Documentelement.clientheight does not do caching, resulting in each critical point to be retrieved:

2 The default value of scrolling callback interval is too large, it should be set smaller, this time using is the 5,bootstrap is 1, only in this way to ensure smooth effect;

3 Some scenes may not need to resize the time to reset the width of the sticky element, you should add an option to control;

4 when the sticky element is fixed and canceled, a callback function should be provided so that other components can rely on the component to do something at the key point.

2. How to Improve

The options for the component are redefined:

var DEFAULTS = {
target: ', the JQ selector type of the//target element
: ' top ',///fixed position, tops | bottom, default to top-level, indicating fixed at the front
Wait:5,//SCR Oll Event callback Interval
stickyoffset:0,//fixed-time offset from the top or bottom of the browser's visual area, to set the value of the top-level and bottom properties, default to 0
isfixedwidth:true,/ Sticky the element width is fixed, the default is true, and if it is an adaptive width, set to False
getstickywidth:undefined//To get a callback for the width of the sticky element, without passing the argument Stickywidth will be set to the offsetwidth unstickydistance:undefined of the sticky element
,//This parameter determines when the sticky element enters the Dynamicsticky state
onsticky:undefined, the///sticky element is fixed when the callback
onunsticky:undefined the///sticky element cancels the fixed callback
};

Some of the bold are new or modified, removed the original height, with unstickydistance to replace. Fixed time relative to the top or bottom of the browser position, with Stickyoffset to specify, so in the. sticky--in-top or. Sticky--in-bottom css, you do not have to write top or bottom property values. Isfixedwidth If False, a callback that refreshes the width of the sticky element when the resize is added:


!opts.isfixedwidth && $win. Resize (throttle () (function () {
setstickywidth ();
$elem. Hasclass (className) && $elem. css (' width ', stickywidth);
Sticky ();
}, opts.wait);

Compared to the last time, the trouble is to cancel the fixed logic processing, the last sticky element only 2 states, sticky or unsticky, this time different, sticky state is divided into Staticsticky and Dynamicsticky, The former represents the sticky state of the top or bottom value unchanged, which indicates that the top or bottom value will change the sticky state, in fact, the latter is about to cancel the fixed time of the range, in order to solve this problem more clearly, The code that makes the original judgment critical point and does different processing at different tipping points will form the following:

Setsticky = function () {! $elem. Hasclass (className) && $elem. addclass (className). css (' width ', stickywidth) &
amp;& (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) & Lt
Docclientheight) return ' Staticsticky '; else if ((Rect.top + unstickydistance) >= docclientheight && rect.top < Docclientheight) return ' dynamicstic
KY ';
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); }

A bit of state-mode thinking is inside, but more concise. When I write this code, actually very want to use before the state machine to write, I think the state machine to write is certainly achievable, but in order to less reference a class library even if you want to practice the state machine time to try again.

The overall implementation is as follows:

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); } var DEFAULTS = {target: ',//target element's JQ selector type: ' Top ',///fixed position, tops | Bottom, default top-level, indicates fixed on the front wait:5,//scroll event callback stickyoffset:0,//fixed-time offset from the top or bottom of the browser's visual area, used to set the value of the top-level and bottom properties, the default is 0 isfixedwidth:true,//sticky element width is fixed, default is true,
If it is an adaptive width, it needs to be set to false getstickywidth:undefined//To get a callback for the width of the sticky element, without which the stickywidth will be set to the sticky element's offsetwidth unstickydistance:undefined,//This parameter determines when the sticky element enters the Dynamicsticky state onsticky:undefined, and the callback///sticky when the element is fixed:
Undefined///sticky element cancels the fixed callback}; 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].off
SetWidth; }, 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 to ' 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 ' dynamicstic
KY ';
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);

It may be difficult to understand the logic of the GetState method, and some of the ideas in this section are described in more detail in the previous blog.

3. Blog side Sidebar Application instructions

First you have to paste the implementation into the blog Set footer HTML text field, and then add the following code to initialize:

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);

Using a timer is because the contents of the sidebar are Ajax loaded, and it is not possible to add callbacks to these AJAX requests, only to determine if the sidebar is loaded by the content they return.

4. Summary

This weekend figured out how to improve the sticky component, plus write this article, spent most of the day time, at least now this sticky component function with the realization can let oneself a little satisfied with the feeling, the last time I finished always feel strange, as if the shortcomings of what, the original is because there are so many things. Now this component is only able to achieve the fixed and cancel the fixed effect, for the actual work, the effect of this level may not be enough, the common online in the same time to support navigation scrolling or tab navigation features are also very common, the next article will introduce the sticky components based on this article, Please pay attention to how to implement Navscrollsticky and Tabsticky components.
Thank you for your reading:

Supplementary Note:
IE and Firefox inside, when refreshing the page, if the page before the refresh is scrolling, the refresh operation will also set the scrolling position of the page to the location of the refresh, but will not trigger the scroll event, so must be called immediately after the component initialization sticky function:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.