How does JavaScript determine whether an element is in the visual area?

1, Use

The visible area is the area visible to the naked eye of our web browsing device, as shown in the figure below

In daily development, we often need to judge whether the target element is within the window or the distance from the window is less than a value (e.g. 100 px), so as to realize some common functions, such as:

  • Lazy loading of pictures

  • Infinite scrolling of lists

  • Calculate the exposure of advertising elements

  • Clickable link preload

2, Implementation mode

There are three common methods to judge whether an element is in the visual area:

  • offsetTop,scrollTop

  • getBoundingClientRect

  • Intersection Observer

offsetTop,scrollTop

offsetTop, the pixel distance from the upper outer border of the element to the upper inner border containing the element. Other offset attributes are shown in the following figure:

Let's learn more about clientWidth and clientHeight:

  • clientWidth: the width of the element content area plus the width of the left and right inner margins, that is, clientWidth = content + padding

  • clientHeight: the height of the element content area plus the height of the upper and lower inner margins, that is, clientHeight = content + padding

You can see that none of the client elements include the outer margin

Finally, the properties of the scroll series are as follows:

  • scrollWidth   and   scrollHeight   It is mainly used to determine the actual size of element content

  • scrollLeft   and   scrollTop   Property can determine the current scrolling state of an element or set the scrolling position of an element

    • Vertical scroll   scrollTop > 0

    • horizontal scroll   scrollLeft > 0

  • The element's   scrollLeft   and   scrollTop   Set to 0 to reset the scrolling position of the element

be careful

  • The above properties are read-only, and each access must be restarted

Here's how to realize judgment:

The formula is as follows:

el.offsetTop - document.documentElement.scrollTop <= viewPortHeight

Code implementation:

function isInViewPortOfOne (el) {
    //  viewPortHeight   Compatible with all browser writing methods
    const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
    const offsetTop = el.offsetTop
    const scrollTop = document.documentElement.scrollTop
    const top = offsetTop - scrollTop
    return top <= viewPortHeight
}

getBoundingClientRect

The return value is a   DOMRect object with left,   top,   right,   bottom,   x,   y,   width, and   height attribute

const target = document.querySelector('.target');
const clientRect = target.getBoundingClientRect();
console.log(clientRect);

// {
//   bottom: 556.21875,
//   height: 393.59375,
//   left: 333,
//   right: 1017,
//   top: 162.625,
//   width: 684
// }

The corresponding relationship diagram of attributes is as follows:

When the page scrolls, the top and left attribute values will change accordingly

If an element is in the window, it must meet the following four conditions:

  • top is greater than or equal to 0

  • left is greater than or equal to 0

  • bottom is less than or equal to the window height

  • right is less than or equal to the window width

The implementation code is as follows:

function isInViewPort(element) {
  const viewWidth = window.innerWidth || document.documentElement.clientWidth;
  const viewHeight = window.innerHeight || document.documentElement.clientHeight;
  const {
    top,
    right,
    bottom,
    left,
  } = element.getBoundingClientRect();

  return (
    top >= 0 &&
    left >= 0 &&
    right <= viewWidth &&
    bottom <= viewHeight
  );
}

Intersection Observer

Intersection Observer   That is, the overlap observer. From this name, we can see that it is used to judge whether two elements overlap. Because there is no event listening, the performance is much better than getBoundingClientRect

The usage steps are mainly divided into two steps: creating an observer and passing in an observer

Create observer

const options = {
  //  Represents the proportion of overlapping area in the observed, from   0  -  one   Value,
  //  one   Indicates full inclusion
  threshold: 1.0, 
  root:document.querySelector('#scrollArea')  //  Must be a parent of the target element
};

const callback = (entries, observer) => { ....}

const observer = new IntersectionObserver(callback, options);

The observer is created through the new IntersectionObserver   Observer, parameters passed in   callback   When the overlap ratio exceeds   threshold   Will be executed`

The common attributes of the callback function are as follows:

//  Omitted from the previous code   callback
const callback = function(entries, observer) { 
    entries.forEach(entry => {
        entry.time;               //  Trigger time
        entry.rootBounds;         //  The position rectangle of the root element, in this case the window position
        entry.boundingClientRect; //  The position of the observer is held
        entry.intersectionRect;   //  Position rectangle of overlapping area
        entry.intersectionRatio;  //  The proportion of overlapping area in the area of the observed person (if the observed person is not rectangular, it is also calculated according to the rectangle)
        entry.target;             //  Observed
    });
};

Incoming observer

adopt   observer.observe(target)   This line of code can simply register the observer

const target = document.querySelector('.target');
observer.observe(target);

3, Case analysis

Implementation: a long list of 100000 nodes is created. When the nodes roll into the window, the background will change from red to yellow

The Html structure is as follows:

<div class="container"></div>

css style is as follows:

.container {
    display: flex;
    flex-wrap: wrap;
}
.target {
    margin: 5px;
    width: 20px;
    height: 20px;
    background: red;
}

Insert 1000 elements into container

const $container = $(".container");

//  insert   one hundred thousand   individual  < div   class="target"></div>
function createTargets() {
  const htmlString = new Array(100000)
    .fill('<div class="target"></div>')
    .join("");
  $container.html(htmlString);
}

Here, first use the getBoundingClientRect method to determine whether the element is in the visual area

function isInViewPort(element) {
    const viewWidth = window.innerWidth || document.documentElement.clientWidth;
    const viewHeight =
          window.innerHeight || document.documentElement.clientHeight;
    const { top, right, bottom, left } = element.getBoundingClientRect();

    return top >= 0 && left >= 0 && right <= viewWidth && bottom <= viewHeight;
}

Then start listening to the scroll event to determine which elements on the page are in the visual area. If they are in the visual area, set the background color to yellow

$(window).on("scroll", () => {
    console.log("scroll !");
    $targets.each((index, element) => {
        if (isInViewPort(element)) {
            $(element).css("background-color", "yellow");
        }
    });
});

Through the above methods, we can see that the color of the visual area will turn yellow, but we can obviously see the phenomenon of jamming. The reason is that we bind the scroll event. The scroll event is accompanied by a large number of calculations, which will cause a waste of resources

The same functions are also realized in the form of Intersection Observer

First create an observer

const observer = new IntersectionObserver(getYellow, { threshold: 1.0 });

The getYellow callback function changes the background color as follows:

function getYellow(entries, observer) {
    entries.forEach(entry => {
        $(entry.target).css("background-color", "yellow");
    });
}

Finally, the observer is passed in, that is, the. target element

$targets.each((index, element) => {
    observer.observe(element);
});

You can see that the function is also completed, and the page will not get stuck

Tags: Javascript

Posted on Wed, 20 Oct 2021 20:00:18 -0400 by KendersPlace