[Front-end dictionary] Comparison of 4 ways to achieve rolling ceiling

Preface

The first request I received from the second company I joined was to repair the rolling ceiling effect that was previously outsourced. I was wondering why there is a bug in a scrolling ceiling. Later I checked the code and found that the attribute offsetTop was used directly, and no compatibility treatment was done.

offsetTop

Used to get the distance (offset value) from the current element to the top of the positioning parent (element.offsetParent ).

The definition of positioning parent offsetParent is: the parent element of the nearest position != static to the current element.

Perhaps the person who wrote this code did not notice the sub-condition of "locating the parent".

Later, in the project, there will always be the effect of rolling ceiling that needs to be realized. Now I will give a detailed introduction to the 4 methods of rolling ceiling that I know.

table of Contents

  1. Use position:sticky to achieve

  2. Use JQuery's offset().top implementation

  3. Use native offsetTop implementation

  4. Use obj.getBoundingClientRect().top to achieve

Do you know all the above four methods? The relevant code has been uploaded to GitHub ( https://github.com/wanqihua/The-dictionary-of-front-end-test), and those interested can clone the code and run it locally. Hope to give a star support.

Four ways to achieve

One, use position: sticky to achieve

1. What is sticky positioning?

Sticky positioning sticky is equivalent to the combination of relative positioning relative and fixed positioning; during the scrolling process of page elements, when the distance between an element and its parent element reaches the sticky positioning requirement; the relative effect of the relative positioning of the element becomes fixed positioning fixed Effect.

2. How to use it?

Conditions of Use:

  1. The parent element cannot have overflow: hidden or overflow: auto attributes

  2. Must specify one of top, bottom, left, right 4 values, otherwise it will only be in relative positioning

  3. The height of the parent element cannot be lower than the height of the sticky element

  4. The sticky element only takes effect within its parent element

This effect can be achieved by adding the following styles to the elements that need to be scrolled and ceiling:

.sticky {

    position: -webkit-sticky;

    position: sticky;

    top: 0;

}

3. Is this attribute easy to use?

Let's first take a look at the compatibility of this attribute in Can I use:
[Front-end dictionary] Comparison of 4 ways to achieve rolling ceiling

It can be seen that the compatibility of this attribute is not very good, because this API is only an experimental attribute. However, the compatibility of this API in the IOS system is still relatively good.

Therefore, if we use this API in a production environment, we will generally use it in combination with the following methods.

Second, use JQuery's offset().top implementation

We know that JQuery encapsulates the API for manipulating DOM and reading DOM calculation properties. Based on the combination of offset().top and scrollTop(), we can also achieve the scrolling ceiling effect.

...

window.addEventListener('scroll', self.handleScrollOne);

...

handleScrollOne: function() {

    let self = this;

    let scrollTop = $('html').scrollTop();

    let offsetTop = $('.title_box').offset().top;

    self.titleFixed = scrollTop > offsetTop;

}

...

This is certainly possible, but since JQuery is slowly withdrawing from the stage of history, we try not to use JQuery API in the code. We can handle the native offsetTop ourselves based on the source code of offset().top. So there is a third way.

scrolloTop() has compatibility issues. In WeChat browsers, IE, and some firefox versions, the value of $('html').scrollTop() will be 0, so there is almost no compatibility of the third scheme .

Third, use the native offsetTop implementation

We know that offsetTop is the offset relative to the positioning parent. If the element that needs to scroll to the top appears to position the parent element, then offsetTop does not get the distance between the element and the top of the page.

We can do the following processing on offsetTop ourselves:

getOffset: function(obj,direction){

    let offsetL = 0;

    let offsetT = 0;

    while( obj!== window.document.body && obj !== null ){

        offsetL += obj.offsetLeft;

        offsetT += obj.offsetTop;

        obj = obj.offsetParent;

    }

    if(direction === 'left'){

        return offsetL;

    }else {

        return offsetT;

    }

}

use:

...

window.addEventListener('scroll', self.handleScrollTwo);

...

handleScrollTwo: function() {

    let self = this;

    let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;

    let offsetTop = self.getOffset(self.$refs.pride_tab_fixed);

    self.titleFixed = scrollTop > offsetTop;

}

...

Do you see some problems with the above two methods?

Do we need to use the value of scrollTop-offsetTop to achieve the effect of scrolling ceiling? the answer is negative.

Let's take a look at the fourth option.

Fourth, use obj.getBoundingClientRect().top to achieve

definition:

This API can tell you the distance of an element on the page relative to the browser window.

use:

For tab ceiling, you can use obj.getBoundingClientRect().top instead of scrollTop-offsetTop, the code is as follows:

// html

<div class="pride_tab_fixed" ref="pride_tab_fixed">

    <div class="pride_tab" :class="titleFixed == true ? 'isFixed' :''">

        // some code

    </div>

</div>

// vue

export default {

    data(){

      return{

        titleFixed: false

      }

    },

    activated(){

      this.titleFixed = false;

      window.addEventListener('scroll', this.handleScroll);

    },

    methods: {

      //滚动监听,头部固定

      handleScroll: function () {

        let offsetTop = this.$refs.pride_tab_fixed.getBoundingClientRect().top;

        this.titleFixed = offsetTop < 0;

        // some code

      }

    }

  }

The difference between offsetTop and getBoundingClientRect()

1. getBoundingClientRect():

Used to get the position of the left, top, right and bottom of an element in the page relative to the browser window. Does not include the rolled-up part of the document.

This function returns an object object with 6 attributes: top, right, buttom, left, width, height. (In IE, the default coordinates are calculated from (2,2), and only four values ​​of top, lef, right, and bottom are returned)

2. offsetTop:

Used to get the distance (offset value) from the current element to the top of the positioning parent (element.offsetParent ).

The definition of positioning parent offsetParent is: the parent element of the nearest position != static to the current element.

The offsetTop and offsetParent methods are combined to get the distance from the element to the top margin of the body. code show as below:

getOffset: function(obj,direction){

    let offsetL = 0;

    let offsetT = 0;

    while( obj!== window.document.body && obj !== null ){

        offsetL += obj.offsetLeft;

        offsetT += obj.offsetTop;

        obj = obj.offsetParent;

    }

    if(direction === 'left'){

        return offsetL;

    }else {

        return offsetT;

    }

}

Extended knowledge

offsetWidth:

The size of the space occupied by the element in the horizontal direction:
offsetWidth = border-left + padding-left + width + padding-right + border-right

offsetHeight:

The size of the space occupied by the element in the vertical direction:
offsetHeight = border-top + padding-top + height + padding-bottom + border-bottom

Note: If there is a vertical scroll bar, offsetWidth also includes the width of the vertical scroll bar; if there is a horizontal scroll bar, offsetHeight also includes the height of the horizontal scroll bar;

offsetTop:

The pixel distance between the upper outer border of the element and the upper inner border of the offsetParent element;

offsetLeft:

The pixel distance from the left outer border of the element to the left inner border of the offsetParent element;

Precautions

  1. All offset attributes are read-only;

  2. If display:none is set for the element, its offset attribute is all 0;

  3. Every time you access the offset attribute, you need to recalculate (save the variable);

  4. During use, it may appear that the DOM is not initialized, and the attribute is read. At this time, 0 will be returned; for this problem, we need to wait until the DOM element is initialized before executing.

Two problems encountered

1. The moment of suction is accompanied by jitter

The reason for the jitter is because: when the top element position becomes fixed, the element is out of the document flow, and the next element is filled. It is this bit-filling operation that causes jitter.

solution

Add a parent element of equal height to this ceiling element. We monitor the getBoundingClientRect().top value of this parent element to achieve the ceiling effect, namely:

<div class="title_box" ref="pride_tab_fixed">

    <div class="title" :class="titleFixed == true ? 'isFixed' :''">

    使用 `obj.getBoundingClientRect().top` 实现

    </div>

</div>

This solution can solve the jitter bug.

Second, the ceiling effect cannot be responded in time

This problem is my headache. I didn't care about it before. I didn't pay attention to this problem until one day when I used Meituan to order takeout.

description:

  1. When the page scrolls down, the ceiling element needs to wait for the page scrolling to stop before the ceiling effect appears

  2. When the page is scrolled up, the ceiling element will not return to its original shape when scrolling to the ceiling element to restore the document flow position, but will return to its original shape after the page stops scrolling

Reason: The scroll monitoring event cannot be monitored in real time on the ios system, and its related events are triggered when the scrolling stops.

solution:

Remember the position: sticky in the first scheme? This attribute has good compatibility in systems above IOS6, so we can distinguish between IOS and Android devices to do two processing.

IOS uses position:sticky, and Android uses scrolling to monitor the value of getBoundingClientRect().top.

What if the IOS version is too low? Here is an idea: window.requestAnimationFrame().

Front-end dictionary series

The "Front-end Dictionary" series will continue to be updated. In each issue, I will talk about a knowledge point that appears frequently. I hope that you will be able to find any imprecise or wrong places in the text during the reading process. I will be very grateful; if you can gain something through this series, I will also be very happy.

Content: The introduction of front-end and network-related knowledge points, with practical application as an aid.

Purpose: This series of articles can help readers a little and solve some confusion.

I hope you can give me some pointers and give me advice.

If you think my article is well written, then follow me!

Guess you like

Origin blog.51cto.com/15077552/2596506