Use the getBoundingClientRect method to implement simple sticky component method_javascript skills

Source: Internet
Author: User
This article introduces the implementation of this component and provides a specific implementation that supports sticky elements fixed at the top or bottom. Because this component is very common on websites, therefore, it is necessary to master its implementation method so that sticky components can be written out based on its ideas when necessary, which is usually used in navigation bars or toolbar, when a webpage is rolling in a certain area, the elements such as the navigation bar or toolbar are fixed at the top or bottom of the page, so that you can quickly perform operations provided by such elements. This article introduces the implementation of this component and provides a specific implementation that supports sticky elements fixed at the top or bottom. Because this component is very common on websites, therefore, it is necessary to master its implementation methods so that more components can be written based on its ideas as needed.

Fixed demo (sticky-top.html ):

Basic demo (sticky-bottom.html ):

1. Implementation ideas

The key to implementing this component is to find the critical point when the element is fixed and when it is canceled. To find this critical point, first look at the changes in the previous demo. In the previous demo, there was a navigation bar element, that is, the element that we want to control whether it is fixed or not. I call it a sticky element, and an element that is used to display a list of webpages, this list element is functionally related to the sticky element, because the sticky element is the content provided by this list element. This article introduces the functions of the sticky component, it is said that the sticky component is fixed when the webpage scrolls to a certain area, and the unit will be canceled when the page leaves this area. This rolling area or rolling range, the list element is determined by the list element. Therefore, this list element is the key to finding the critical point. It indicates that the sticky component can be fixed to the page rolling range for convenience of subsequent reference, I call this element the target element. Next, let's take a closer look at the change process of the previous demo. Because the fixed conditions at the bottom are the same as those at the top, if you understand the implementation principle of fixed at the top, I believe you will be able to understand the implementation principle of fixed at the bottom, so this is also to reduce space and improve efficiency, this section only describes the fixed position on the top:

At the beginning, the status of the sticky and target elements is as follows:

When the scroll bar slowly goes down to make the page scroll up, the status of the sticky element and the target element does not change within a certain scroll distance until this State (the scroll distance of the scroll bar is 573px ):

In this state, as long as the scroll bar is scrolled down 1px, the sticky element will be fixed at the top (the scroll distance of the scroll bar is 574px ):

That is to say, when the distance between the top of the target element and the top of the browser is less than 0 (when the top of the target element is not greater than the top of the browser, the distance is regarded as greater than 0), the sticky element will be fixed, so this is the first critical point we are looking. Then the scroll bar continues to scroll down. As long as the target element is still in the visible area of the browser, the sticky element will always be fixed:

Until this status (the scroll distance of the scroll bar is 1861px ):

In this state, as long as the scroll bar is scrolled down by 1px, the sticky element will be unfixed at the top (the scroll distance of the scroll bar is 1862px ):


Obviously, this is the 2nd critical point we are looking for, but its condition is that when the distance from the bottom of the target element to the top of the browser is less than the height of the sticky element, the sticky element is unfixed. Here, why is it less than the height of the sticky element, rather than less than 0, because the components developed based on the critical point of less than 0 will almost disappear from the visible area of the browser, but the sticky element is still fixed there:


Sticky also covered the footer content. It was originally intended to facilitate user operations and the results affected user operations. Therefore, the critical point of unfixing must be advanced, the height of the sticky element is the most suitable.

Through the disassembly of the demo change process, we have obtained two critical points of sticky state change when the scroll bar keeps rolling down:

1) when the distance between the top of the target element and the top of the browser is less than 0, the sticky element will be fixed;

2) when the distance between the bottom of the target element and the top of the browser is less than the height of the sticky element, the sticky element will be unfixed.

Based on these two critical points, it can be concluded that the sticky element is determined by a fixed rolling range when the scroll bar is scrolled down: the distance from the top of the target element to the top of the browser is less than 0, and the distance from the bottom of the target element to the top of the browser is greater than the height of the sticky element. This judgment condition also applies to the scroll bar rolling up, because the critical point of sticky status change when the scroll bar is always rolling up:

1) when the distance between the bottom of the target element and the top of the browser is greater than the height of the sticky element, the sticky element will be fixed;

2) When the distance from the top of the target element to the top of the browser is greater than 0, the sticky element will be unfixed.

(These two critical points are actually the two critical points mentioned when the scroll bar is rolling down. They are just saying the opposite)

Therefore, you only need to get the distance from the top of the target element to the top of the browser, and the distance from the bottom of the target element to the top of the browser ], [height of the sticky element] These three values can basically implement this component. The height of the sticky element in the three values is determined by the design drawing. It is known to be created from the beginning of the webpage. When defining components, we can transfer them from the outside, although it can also get its height from js, there is obviously no need to add additional calculations; the other two values [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], we can use a method provided by DOM to obtain it. This method is getBoundingClientRect, which is a good compatibility method, the call method is as follows:

var target = document.getElementById('main-container');var rect = target.getBoundingClientRect();console.log(rect);

Returns a ClientRect object that stores information about the element box model, such as its width and height ), and the distance between the upper and lower sides of the element box and the top and bottom sides of the browser and the left and right sides of the browser ):


Top and bottom are exactly the distance from the top of the target element to the top of the browser, and the distance from the bottom of the target element to the top of the browser ], in addition, when the top or bottom of the box does not exceed the top of the browser, both top and bottom are greater than 0, and when the top or bottom of the box exceeds the top of the browser, top and bottom are smaller than 0 values:

When we find 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, and the height of the sticky element, you can use the code to describe the previous judgment conditions:

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

(Rect indicates that the target element calls the object returned by getBoundingClientRect, and stickyHeight indicates the height of the sticky element)

Finally, in order to make the implementation idea more complete, although I do not detail the change process fixed at the bottom, I will add the critical point of the situation and the judgment method, the critical point is (here the critical point when the scroll bar is scroll down ):

1) when the distance from the top of the target element to the top of the browser + the height of the sticky element is smaller than the height of the visible area of the browser, the sticky element is fixed;

2) when the distance between the bottom of the target element and the top of the browser is smaller than the height of the visible area of the browser, the sticky element is unfixed.

You can use document.doc umentElement. clientHeight to obtain the page view height. This attribute has no compatibility problems and the code is:

var docClientWidth = document.documentElement.clientHeight;rect.bottom > docClientWidth && (rect.top + stickyHeight) < docClientWidth;

2. Implementation Details

1) html Structure

Fixed html structure on top:

  • Home
  • Profile
  • Messages

...

...

Fixed html structure at the bottom:

...

...

  • Home
  • Profile
  • Messages

The above # main-container is our target element. # sticky is our sticky element. Pay attention to the following two points:

A. Order problem. In the two structures, the Order position of the parent element of the target element and sticky is reversed;

B. The sticky element must be enclosed by a layer of elements, and the height attribute must be set for this layer of elements:

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

This is because when the sticky element is fixed, it will be out of the normal Document Stream, so the height of the sticky element should be supported in the normal Document Stream using its parent element, this prevents the content of the target element from beating when a fixed effect occurs.

2) fixed Effect

To fix an element to a certain position in the browser, of course, it is achieved through position: fixed, so we can use two css classes to achieve the effect of being fixed at the top and fixed at the bottom:

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

When we determine that the element needs to be fixed at the top, we will add it. sticky -- in-top css class; when we determine that the element needs to be fixed at the bottom, we will add it. sticky -- in-bottom css class.

3) rolling callback

The logic for controlling sticy elements should be clearly written in the scroll Event Callback of window (with the Implementation ideas and judgment conditions described earlier, I believe the following code should be easy to understand ):

Callback logic fixed at the top:

$(window).scroll(function() {var rect = $target[0].getBoundingClientRect();if (rect.top < 0 && (rect.bottom - stickyHeight) > 0) {!$elem.hasClass('sticky--in-top') && $elem.addClass('sticky--in-top').css('width', stickyWidth + 'px');} else {$elem.hasClass('sticky--in-top') && $elem.removeClass('sticky--in-top').css('width', 'auto');}});

$ 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 stickyWidth is the width of the sticky element. When the sticky element is fixed, you need to set the width of the original document stream to display the same width as before.

The callback logic is 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');}});

Here, the code is divided into two parts to make the callback logic clearer. The final implementation will merge the two codes into one copy :)

4) function throttling

Function Throttling is usually applied to scroll events of windows, resize events, and mousemove events of common elements. These events are triggered consecutively due to frequent mouse or scroll wheel operations, if the callback contains DOM operations, this continuous call will affect the page performance. Therefore, it is necessary to control the number of executions of such Callbacks. Function Throttling is done in this way, 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, and set the execution interval to the number of milliseconds specified by wait. With this function, we can change the previous rolling callback, for example, if it is fixed on the top, change it:

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

In fact, the real processing callback is the function returned by the throttle. The returned function has less logic and no DOM operation. It will be called continuously without affecting the page performance, the function that actually processes the logic, that is, the function that passes in the throttle, is not called continuously because of the function of the closure created by the throttle. This achieves the purpose of controlling the number of function executions.

5) resize Problems

Window resize always brings problems when defining components. Because the width and height of the visible area of the page have changed, the width of the parent container of the sticky element may also change, in addition, the scroll event is not triggered during resize, so we need to refresh the width of the sticky element and re-call the logic of the fixed effect in the resize callback, and the related code will not be pasted out, next, let's look at the overall implementation. Otherwise, I'm afraid it will affect my understanding. In short, resize is something we must consider when defining components, but it is usually put at the end of the process, a bit of work such as fixing bugs.

3. Overall Implementation

The code is concise:

/*** @ Param elem: jquery selector, used to obtain the element to be fixed * @ param opts: *-target: jquery selector, used to obtain the element *-type: top | bottom that indicates the position *-height: height of the element to be fixed, because the height is determined when the page is made and is almost not changed by DOM operations, you can directly import an operation from the outside to remove the operation to get the element height *-wait: the throttling time of the rolling Event Callback, control how long the callback will take at least once *-getStickyWidth: gets the width of the elements to be fixed. window resize or DOM operations will change the width of the fixed elements, this callback is required to refresh stickyWidth */var Sticky = function (elem, opts) {var $ elem =$ (elem), $ target = Response (opts.tar get | $ ele M. 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.doc umentElement. clientHeight; return rect. bottom> docClientWidth & (rect. top + stickyHeight) <docClientWidth ;}, type = (opts. type in rules) & opt S. 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) & $ ele M.css ('width', stickyWidth + 'px ');} // function sticky () {if (rules [type] ($ target [0]. getBoundingClientRect ())){! $ Elem. hasClass (className) & $elem.addClass(className).css ('width', stickyWidth + 'px');} else {$ elem. hasClass (className) & policelem.removeclass(classname).css ('width', 'auto') ;}// 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 );}}};

Call method, fixed at the top (the default type option is top ):

《script》new Sticky('#sticky',{height: 52,getStickyWidth: function($elem){return ($elem.parent()[0].offsetWidth - 30);}});《script》

Fixed at the bottom:

《script》new Sticky('#sticky',{height: 52,type: 'bottom',getStickyWidth: function($elem){return ($elem.parent()[0].offsetWidth - 30);}});《script》

The getStickyWidth option of opts is also worth noting. This callback is used to obtain the width of the sticky element. Why do we need to release it and obtain the width from the outside, instead of getting it through offsetWidth inside the component? When the external container of the sticky element is adaptive, the width of the sticky element is not determined by the sticky element, but by the width of the external container, therefore, the width can only be obtained externally, and the internal retrieval is inaccurate. For example, in the code above, I subtract 30. If I get it inside the component, I certainly don't know how to add a logic like 30.

4. Summary

This article provides a common sticky component implementation. The key to implementing this component is to find the key point to control whether the sticky element is fixed or not, at the same time, pay special attention to the function throttling and window resize issues during implementation.

I have always thought that for some simple components, the idea of mastering it is more practical to define by myself than to find open-source plug-ins directly from github:

1) the code is controllable and can be quickly modified without having to read other people's code.

2) If the amount of code is small, open-source plug-ins will do as much work as possible, and some projects do not necessarily need to do it;

3) better fit with the actual needs of the project, similar to the meaning of. Based on the existing ideas, we can develop functional modules that fully fit the project requirements;

4) helps improve your technical skills and increase the breadth and depth of knowledge;

Therefore, it is necessary to create a wheel.

Although the entire component implementation is provided at the end of this article, it is not recommended to use it. Otherwise, it is unnecessary to introduce the Implementation ideas in the previous sections, I only need to put a github address. The idea is far more important than implementation. I have been sharing ideas in my recent blogs, rather than sharing specific implementations. abstract things such as ideas are generic and are not yours before I understand them, after understanding it, it will exist in my head and can be used at any time. The idea I provide also comes from my thoughts and conclusions on other blog plug-in source code learning.

Additional instructions:

The implementation in this article is inadequate and not perfect. After learning about the content in this article, please read "sticky component improvement implementation" to learn about better implementation.

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.