Methods to implement concise sticky components using the Getboundingclientrect method _javascript techniques

Source: Internet
Author: User
Tags wrapper

A sticky component, usually applied to a navigation bar or toolbar, that uses elements such as navigation bars or toolbars at the top or bottom of a page when scrolling in an area, making it easy for users to quickly perform the actions provided by such elements. This paper introduces the realization of this kind of component, and provides a concrete implementation that supports fixing the sticky element at the top or bottom, because it's very common in the Web site, so it's important to know how it's implemented so that you can write out more components based on its ideas when you need it.

Fixed at the top of the demo effect (corresponding sticky-top.html):

Fixed at the bottom of the demo effect (corresponding to sticky-bottom.html):

1. Realize the idea

The key to the implementation of this component is to find out when the element is fixed and when the fixed critical point, to find this critical point, first of all to look at the previous demo of the change process. In the previous demo, there is a navigation bar element that we want to control the fixed or not element, I call it the sticky element, and an element that displays a list of pages that are functionally related to the sticky element, Since the sticky element is to navigate exactly what this list element provides, this article begins by introducing the functionality of the sticky component when it is said that the sticky component is fixed when the page is scrolled to a certain area, leaving the area to be fixed, the scrolling area or the scrolling range, is determined by the list element, so this list element is the key to finding the critical point, which means that the sticky component can be fixed to the scrolling range of the Web page, which I refer to as the target element for later reference. Here is a detailed understanding of the previous demo of the change process, because the fixed at the bottom of the situation and fixed at the top of the situation to achieve the idea is interlinked, if you understand the fixed at the top of the implementation principle, I believe you will be able to understand the fixed at the bottom of the implementation principle, so here is also to reduce space, improve efficiency, Just the case of fixing at the top:

The state of the sticky element and target element at the outset is like this:

When the scroll bar is slowly downward, making the page scroll up, the sticky element and the target element have not changed within a rolling distance, all the way up to this state (scroll bar scrolling distance is 573px):

In this state, as long as the scroll bar is scrolled down, the 1px,sticky element will be pinned to the top (the scroll bar has a scrolling distance of 574px):

That is, when the top of the target element is less than 0 from the top of the browser (the target element is not above the top of the browser, the distance is considered to be greater than 0), the sticky element is fixed, so this is the first critical point we are looking for. The scroll bar then continues to scroll down, and as long as the target element is still in the viewable area of the browser, the sticky element is fixed:

Until this state (scroll bar scrolling distance is 1861px):

In this state as long as the scroll bar and then scroll down the 1px,sticky element will be canceled to the top (scroll bar scrolling distance of 1862px):


Obviously, this is the 2nd critical point we are looking for, but it is judged that when the target element's bottom is less than the sticky element's height, the sticky element is canceled. Why this is less than the height of the sticky element, not less than 0, because the target element will almost disappear from the browser's viewable area because of components developed based on a critical point less than 0, but the effect of the sticky element is fixed there:


Sticky also footer content to cover, originally is to facilitate user operation, the result affected user operation, so have to cancel fixed this critical point ahead, and with sticky element height most suitable.

We have obtained the two critical points of the sticky state change when the scroll bar has been scrolled down by the solution before the demo change process:

1 when the top of the target element is less than 0 from the top of the browser, the sticky element is fixed;

2 the sticky element is sticky when the target element's bottom distance from the top of the browser is less than the height of the element.

Combining these two critical points, you can conclude that when the scroll bar rolls down, the sticky element is determined by the fixed scrolling range: The top of the target element is less than 0 away from the top of the browser and the target element's bottom away from the top of the browser is greater than the sticky element's height. Also, this criterion applies to scrolling up the scroll bar, because when the scroll bar is scrolling up, the critical point of the sticky state change is:

1 The sticky element is fixed when the target element's bottom distance from the top of the browser is greater than the height of the sticky element;

2 when the top of the target element is greater than 0 from the top of the browser, the sticky element is canceled.

(These two tipping points, in fact, scroll down with the scroll bar when the two points mentioned in the point, is a meaning, but is Zhenghua to say it)

So as long as you get "the distance from the top of the target element to the top of the browser", "the distance from the bottom of the target element to the top of the browser", the "height of the sticky element" three values basically implement this component. The height of the sticky element in these three values is determined by the design diagram, which is known from the beginning of the Web page, and can be passed from the outside when defining the component, although it can be obtained from JS, though it is obviously not necessary to add additional calculations; two other values " The distance from the top of the target element to the top of the browser, "the distance from the top of the target element to the bottom of the browser", we can just take advantage of a method provided by Dom to get it: Getboundingclientrect, which is a good compatibility method, It is invoked in the following ways:

var target = document.getElementById (' Main-container ');
var rect = Target.getboundingclientrect ();
Console.log (rect);

Returns a Clientrect object that stores some information about the element box model, such as its width height (width and height), and the distance (top and bottom) from the bottom edge of the browser to the upper and lower edges of the box, from the left edge of the browser ( Left and right):


Top and bottom exactly what we're going to get "the distance from the top-level of the target element to the top of the browser", the distance at the bottom of the target element from the top of the browser, and top and bottom are values greater than 0 when the tops or bottoms of the boxes do not exceed the top of the browser. And when the top or bottom of the box is above the top of the browser, tops and bottom are values less than 0:

When we found "the distance from the top of the target element to the top of the browser", "the distance from the bottom of the target element to the top of the browser", "the height of the sticky element" three values, you can use code to describe the previous judgment conditions:

Rect.top < 0 && (rect.bottom-stickyheight) > 0;

(Rect represents the object that the target element invokes Getboundingclientrect returns, Stickyheight represents the height of the sticky element)

Finally, in order to make the implementation more complete, although not in detail fixed at the bottom of the change process, I would like to add the critical point of this situation and the way to judge, its critical point is (here is the scroll bar scroll down when the critical point):

1 The sticky element is fixed when the distance between the top of the target element and the height of the sticky element is less than the height of the viewable area of the browser;

2 The sticky element is canceled when the target element's bottom distance from the top of the browser is less than the height of the browser's viewable area.

The height of the viewable area of the browser can be obtained using Document.documentElement.clientHeight, which is also not a compatibility issue, and the Judgment code is:

var docclientwidth = document.documentElement.clientHeight;
Rect.bottom > Docclientwidth && (rect.top + stickyheight) < Docclientwidth;

2. Implementation Details

1) HTML structure

Fixed at the top of the HTML structure:

<div class= "Container-fluid sticky-wrapper" > <ul id= "sticky" data-target= "#main-container" class= "
Sticky nav nav-pills ">
<li role=" presentation "class=" active "><a href=" # ">home</a></li >
<li role= "presentation" ><a href= "#" >Profile</a></li>
<li role= " Presentation "><a href=" # ">Messages</a></li>
</ul>
</div>
<div ID = "Main-container" class= "Container-fluid" >
<div class= "Row" >
...
</div> ...
</div>

Fixed at the bottom of the HTML structure:

<div id= "Main-container" class= "Container-fluid" >
<div class= "Row" >
...
</div> ...
</div>
<div class= "Container-fluid sticky-wrapper" > <ul id= "sticky"
# Main-container "class=" Sticky nav nav-pills "> <li role=" Presentation "class="
Active "><a href=" # "> home</a></li>
<li role= "presentation" ><a href= "#" >Profile</a></li>
<li role= "Presentation" ><a href= "#" >Messages</a></li>
</ul>
</div>

The above #main-container is our target element, #sticky就是我们的sticky元素, also need to note two points:

A. The order problem, in which the target element is reversed from the sticky of the parent element in the two structures;

B. Sticky elements must be wrapped outside a layer of elements, but also to this layer of elements set the Height property:

. sticky-wrapper {
margin-bottom:10px;
height:52px;
}

This is because when the sticky element is fixed, it will break away from the normal document stream, so use its parent element to prop up the height of the sticky element in the normal document stream, lest the content of the target element bounce when the fixed effect appears.

2) Fixed effect

Keep an element in the browser somewhere, of course, through position:fixed, so you can use two CSS classes to fix the top and fixed at the bottom of the effect:

. sticky--in-top,.sticky--in-bottom {
position:fixed;
z-index:1000
}
. sticky--in-top {
top:0
}
. Sticky--in-bottom {
bottom:0;
}

When we judge that the element needs to be pinned to the top, we add the CSS class to it, and when we judge that the element needs to be anchored to the bottom, we add it to the Sticky--in-top. Sticky--in-bottom CSS class.

3) Scrolling Callback

The logic to control the Sticy element is clearly written in the window's scroll event callback (with a previous description of the implementation idea and the judgment condition, and it should be easy to understand the following code):

Fixed at the top of the callback logic:

$ (window). Scroll (function () {
var rect = $target [0].getboundingclientrect ();
if (Rect.top < 0 && (Rect.bottom-stickyheight) > 0) {
! $elem. Hasclass (' sticky--in-top ') && $el Em.addclass (' Sticky--in-top '). CSS (' width ', stickywidth + ' px ');
} else {
$elem. Hasclass (' sticky--in-top ') && $elem. Removeclass (' Sticky--in-top '). CSS (' width ', ' auto ');
});

Where: $target is the JQ object of the target element, $elem is the JQ object of the sticky element, Stickyheight is the height of the sticky element, and the stickywidth is the width of the sticky element. Because the sticky element is fixed, it needs to be set in width to display the same width as the previous one, leaving the original document stream.

The callback logic fixed at the bottom:

$ (window). Scroll (function () {
var rect = $target [0].getboundingclientrect (),
docclientwidth = Document.documentElement.clientHeight; 
if (Rect.bottom > Docclientwidth && (rect.top + stickyheight) < Docclientwidth) {
! $elem. Hasclass (' Sticky--in-bottom ') && $elem. addclass (' Sticky--in-bottom '). CSS (' width ', stickywidth + ' px ');
} else {
$elem. Hasclass (' Sticky--in-bottom ') && $elem. Removeclass (' Sticky--in-bottom '). CSS (' width ', ' Auto ');
}
);

This is to make the callback logic more clear to divide the code into two parts, the final implementation will combine the two code into one:

4) function throttling

Function throttling is typically applied to window scroll events, resize events, and MouseMove events for ordinary elements, because these events can cause a callback to be triggered continuously because of the frequent mouse or wheel operations, which can affect the performance of the page if the callback contains DOM operations , so it is necessary to control the number of executions of such callbacks, and the function throttling is doing this, and here I provide a very simple function throttling implementation:

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

This function can control the function specified by func, the interval specified as the number of milliseconds specified by wait, using it, we can change the previous scroll callback, such as fixed at the top of the case:

$ (window). Scroll (throttle (function () {
var rect = $target [0].getboundingclientrect (),
docclientwidth = Document.documentElement.clientHeight; 
if (Rect.bottom > Docclientwidth && (rect.top + stickyheight) < Docclientwidth) {
! $elem. Hasclass (' Sticky--in-bottom ') && $elem. addclass (' Sticky--in-bottom '). CSS (' width ', stickywidth + ' px ');
} else {
$elem. Hasclass (' Sticky--in-bottom ') && $elem. Removeclass (' Sticky--in-bottom '). CSS (' width ', ' Auto ');
}
, 50;

What actually handles the callback is the function returned by throttle, which has less logic to return, and does not have a DOM operation, which is called continuously, but does not affect page performance, and the function that we really handle logic That is, the function of the incoming throttle is not called continuously because of the closure created by throttle, thus achieving the purpose of the control function execution times.

5) The Problem of resize

Window resize always causes problems when defining components, because the width of the page's viewable region changes, the width of the parent container of the sticky element may change, and the resize does not trigger the scroll event, so we need to be in the resize callback, Refresh the width of the sticky element and recall the logic of the fixed effect, the relevant code is not posted, the back directly to see the overall implementation, or I am afraid to put out will affect understanding. In short resize is what we must consider when defining components, but it is generally put to the end to deal with, a bit of work to deal with Bugs.

3. Overall realization

The code is relatively concise:

/** * @param elem:jquery selector, which is used to obtain the element to be fixed * @param opts: *-Target:jquery selector, which is used to obtain a fixed range of elements *-type:top|bottom, indicating the position to be fixed *-Height: The height of the element to be fixed, because the height is determined when making the page and is almost not changed by the DOM operation, directly from the outside, you can drop the operation of getting the element height- Ickywidth: Gets the width of the element to be fixed, the window resize or DOM operation causes the width of the fixed element to change, and this callback is required to refresh the stickywidth/var Sticky = function (Elem, opts) {var
$elem = $ (elem), $target = $ (Opts.target | | $elem. DATA (' target '));
if (! $elem. length | |! $target. length) return; var stickywidth, $win = $ (window), stickyheight = Opts.height | | $elem [0].offsetheight, rules = {top:function (rect) {return Rect.top < 0 && (rect.bottom-stickyheight) ;
0; }, Bottom:function (rect) {var docclientwidth = document.documentElement.clientHeight; return rect.bottom > Docclient
Width && (rect.top + stickyheight) < Docclientwidth; }, type = (Opts.type in rules) && Opts.type | |
' Top ', className = ' sticky--in-' + type;
Refreshstickywidth (); $win. Scroll (Throttle (sticKY, $.isnumeric (opts.wait) && parseint (opts.wait) | |
100));
$win. Resize (throttle (function () {refreshstickywidth (); Sticky ();}, 50)); function Refreshstickywidth () {stickywidth = typeof opts.getstickywidth = = ' function ' && opts.getstickywidth ($ Elem) | |
$elem [0].offsetwidth;
$elem. Hasclass (className) && $elem. css (' width ', stickywidth + ' px '); }//Effect implementation function sticky () {if (Rules[type] ($target [0].getboundingclientrect ())) {! $elem. Hasclass (className) &
& $elem. addclass (className). css (' width ', stickywidth + ' px ');
else {$elem. Hasclass (className) && $elem. Removeclass (className). css (' width ', ' auto ');} Functions Throttle 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); }
}
};

Called, fixed at the top of the case (the Type option defaults to tops):

<script>
New Sticky (' #sticky ', {
height:52,
getstickywidth:function ($elem) {return
($ Elem.parent () [0].offsetwidth-30);
}
});
</script>

Fixed at the bottom of the case:

<script>
New Sticky (' #sticky ', {
height:52,
type: ' Bottom ',
getstickywidth:function ($ Elem) {return
($elem. Parent () [0].offsetwidth-30);
}
});
</script>

And one more note, OPTs's getstickywidth option, which is used to get the width of the sticky element, why should I put it out and get the width out of the outside, instead of getting it through the offsetwidth inside the component? Because when the outer container of the sticky element is adaptive, the width of the sticky element is not determined by the sticky element itself, but depends on the width of the outer container, so this width can only be obtained externally and the internal acquisition is inaccurate. For example, I lost a 30 in the code above, and if I get it inside the component, I definitely don't know the logic to add minus 30.

4. Summary

This article provides a very common sticky component implementation, the key to the implementation of this component is to find the control of sticky elements of the key points, while the implementation of the function throttling and window resize problems need special attention.

I have always thought that for some simple components, the idea of mastering it is more practical to define yourself than to find Open-source Plug-ins directly from GitHub:

1 code can be controlled, do not have to read other people's code, there are problems can be quickly modified

2 small code, open source Plug-ins will do as much as possible, and some work your project does not necessarily need it to do;

3) More fit the actual needs of the project, with the meaning of the 2nd almost, in the existing ideas on the basis of our ability to develop with the project needs of a complete functional module;

4 Help to improve their technical level, improve the breadth and depth of knowledge;

So it is necessary to build a wheel when it is capable of making it.

Although this article in the end provides the overall implementation of the components, but is not recommended to use, or the front of a large space to introduce the implementation of ideas will not be necessary, I just put a github address can be, the idea is far more important than the realization. My recent blogs are all about sharing ideas, not sharing a specific implementation, thinking of this abstract thing is universal, understand it is not your before, understand it is in the head, any time can be used, I provide the same ideas from other blog plug-in source after learning from my thinking and summary.

Added to the description:

This article realizes has the insufficiency, the imperfect place, please after understanding this article related content, moves to read "The Sticky component improvement Realization" understands the better realization.

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.