Location>code7788 >text

IntersectionObserver + scrollIntoView for elevator navigation.

Popularity:239 ℃/2024-08-07 00:03:59

Elevator navigation, also known as anchor navigation, scrolls the corresponding marked element within the page to the viewport when the anchor element is clicked. And the corresponding anchor point is also highlighted when the element within the page scrolls. Elevator navigation generally puts the anchors on the left and right sides, similar to an elevator. Common elevator navigation effects are as follows, such as in some official documents:

image

image

It may have been used beforegetBoundingClientRect() to determine if an element is in the viewport to achieve a similar effect, but now there's a more convenient way to do it, and that's theIntersectionObserver + scrollIntoView, easy elevator navigation.

scrollIntoView() Introduction

scrollIntoView() method scrolls the element's parent container so that the element appears in the visual area. The default is immediate scrolling with no animation.

If you want to add an animation effect, you can do so:

scrollIntoView({
  behavior: 'smooth' // instant for immediate scrolling
})

It also has two optional parametersblock cap (a poem)inline

block Indicates the vertical alignment of the element to its parent container when it appears in the viewport.inline Indicates the horizontal alignment of the element to its parent container when it appears in the viewport.

They also have four values to choose fromstartcenterend 、nearestThe The default isstart;

scrollIntoView({
  behavior: 'smooth',
  block:'center',
  inline:'center',
})

For block

  • start Aligns the top of the element with the top of the scroll container.

  • center Aligns the center of the element vertically with the center of the scroll container.

  • end Aligns the bottom of the element with the bottom of the scroll container.

For inline

  • start Aligns the left side of the element with the left side of the scroll container.

  • center Aligns the center of the element horizontally with the center of the scroll container.

  • end Aligns the right side of the element with the right side of the container.

(indicates contrast)nearest Whether vertically or horizontally, as long as it appears in the viewport the task is complete. This can be interpreted as making the element appear in the viewport with a minimal amount of movement, (lazy movement). If the element is already fully present in the viewport, no change occurs.

To get a feel for the change, there are four rows and five columns in the scrolling container below, containing everything from the letterA until (a time)T. Clickappear in the viewport button takes the values of the three drop-down boxes as parameters to call thescrollIntoView() Methods.

image

Let's take another look at the settings fornearest Scrolling after

image

When the lettersG When inside the viewport, calling the method scrolls the container without changing it. When theG not completely in the viewport, it will scroll until it appears completely in the viewport.

Check out this full example herescrollIntoView Optional Parameter Practice (codepen)

And scrollIntoView compatibility is great!

image

IntersectionObserver Introduction

The Intersection Observer API provides a way ofsynchronousA method for detecting changes in the viewport intersection of a target element with an ancestor element or a top-level document. That is, the ability to determine whether an element is in the viewport or not, and the ability to listen to the proportion of the visible portion of the element that appears in the viewport, so that we can execute our customized logic.

Since it's asynchronous, it doesn't block the main thread, and performance is naturally better than the previous frequent execution of thegetBoundingClientRect() It's better to determine if an element is in the viewport.

Create an IntersectionObserver

let options = {
  root: (selector),
  rootMargin: "0",
  threshold: 1.0,
};

let observer = new IntersectionObserver(callback, options);

let target = (selector);
(target); //Listen to the target element

This is accomplished by calling theIntersectionObserver The constructor creates a crossviewer. The constructor takes two arguments, a callback function and an optional. In the above example, the callback function is called when the element is fully present (100%) in the viewport.

selectable

  • root The element used as the viewport must be an ancestor of the target. Defaults to the browser viewport.

  • rootMargin The margins around the root, i.e. the size of the viewport that can limit the detection of the root element. The value of the directional size of the peace is commonly usedmargin Same as, for example"10px 20px 30px 40px"(top, right, bottom, left). Only positive numbers increase the root element detection range and negative numbers decrease the detection range.

For example, let's set a scrollable div container to be the root element, with a width and height of 1000px. Setting therootMargin:0 Indicates that the viewport size of the root element is the size of the current viewable area of the root element, i.e., 1000px * 1000px. setrootMargin:25% 0 25% 0 means the top and bottom margins are 25%, then the detection viewport size is 1000px * 500px.

  • threshold A number, or an array of numbers, indicating what percentage of the viewport the target appears in should the observer's callback be executed. If you only want to detect when visibility exceeds 50%, you can use a value of 0.5. If you want the callback to be executed every time the visibility exceeds 25%, you need to specify the array [0, 0.25, 0.5, 0.75, 1]. The default value is 0 (which means that the callback will run as long as one pixel is visible). A value of 1.0 means that the threshold will not be considered passed until every pixel is visible.

callback function

When the target element matches the configuration in the optional, it triggers our defined callback function

let options = {
  root: (selector),
  rootMargin: "0",
  threshold: 1.0,
};

let observer = new IntersectionObserver(function (entries) {
      (entry => {
        
      })
    }, options);

entries represents an array of listened-to target elements, each entry in the array has some of the following values

  • Returns the boundary information of the target element, the value andgetBoundingClientRect() The form is the same.

  • The proportion of target and root elements that intersect, i.e., appear in the detection region.

  • Returns information about the boundaries of the intersection area of the root and target elements, with the values andgetBoundingClientRect() The form is the same.

  • Returns true or fasle, indicating whether or not it appears within the root element detection area

  • Returns information about the boundaries of the root element, the value andgetBoundingClientRect() The form is the same.

  • Returns the target element that appears within the detection area of the root element

  • Returns the timestamp from when the cross-watcher was created to when the target element appeared in the detection area

For example, to detect that 75% of the target elements appear in the detection area do this.

(entry => {
    if( && >0.75){
         
    }
})

Listen to the target element

After creating an observer, one or more target elements are observed.

let target = (selector);
(target);

('div').forEach(el => {
    (el)
})

IntersectionObserver The compatibility is also good:

image

masteredIntersectionObserver + scrollIntoView usage, implementing elevator navigation is simple.

Simply write an elevator navigationhtml cap (a poem)css

<div class="a" style="background:aqua;">Chapter 1</div>
<div class="b" style="background: blueviolet;">Chapter 2</div>
<div class="c" style="background: chartreuse;">Chapter 3</div>
<div class="d" style="background: darkgoldenrod;">Chapter 4</div>
<div class="e" style="background: firebrick;">Chapter 5</div>
<div class="f" style="background: gold;">Chapter 6</div>
<div class="g" style="background: hotpink;">Chapter 7</div>
<ul class="rightBox">
    <li class="aLi"> Chapter 1</li>
    <li class="bLi"> <li class="bLi"> Chapter 2</li>
    <li class="cLi">Chapter 3</li>.
    <li class="dLi">Chapter 4</li>.
    <li class="eLi">Chapter 5</li>.
    <li class="fLi">Chapter 6</li>.
    <li class="gLi">Chapter 7</li>.
</ul>
html,
body {
  width: 100%;
  height: 100%;
  background-color: #fff;
}

ul,li{list-style: none;}

body {
  padding: 20px 0;
}

div{
  width: 60%;
  height: 70%;
  border-radius: 10px;
  margin-left: auto;
  margin-right: auto;
  opacity: 0.4;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30px;
  font-weight: bold;
  color: #000;
}

div+div {
  margin-top: 20px;
}

.rightBox {
  position: fixed;
  right: 20px;
  top: 50%;
  color: teal;
  transform: translatey(-50%);
}

li {
  cursor: pointer;
  box-sizing: border-box;
  border: 1px solid #fff;
  border-radius: 4px;
  padding: 8px 12px;
}

li:hover {
  background: #f5d2c4;
}

.active {
  background: #f5d2c4;
}

Preview below:

image


Step 1: Click on the navigation menu on the right and utilize thescrollIntoView method makes the element corresponding to the content area appear in the visual area.

    let rightBox = ('.rightBox')
    ('click', function (e) {
      let target =  || ;
      if (target && !('rightBox')) {
        ('.' + ('Li', '')).scrollIntoView({
          behavior: 'smooth',
          block: 'center'
        })
      }
    }, false)

image


Step 2: When the page container scrolls, when the target element appears in the detection area, then linkage changes the style of the corresponding navigation.

here arethreshold be set to1, which means that the callback is executed when the target element is fully displayed in the visual area, changing the style of the navigation menu.

let observer = new IntersectionObserver(function (entries) {
  (entry => {
    let target = ('.' + + 'Li')
    if () { // Appears in the detection area
      ('li').forEach(el => {
        if(('active')){
          ('active')
        }
      })
      if (!('active')) {
        ('active')
      }
    }
  })
}, {
  threshold: 1
})

('div').forEach(el => {
  (el)
})

The effect is as follows:

image


The basic requirements are met, but there are some issues with the scrolling process. For example, when switching back and forth between two consecutive elements, the second element is displayed in a higher percentage of the detection area than the first element, although it doesn't reach 100%, and the navigation menu is still displayed for the first element. See the picture below:

image


So here you can control it more finely, highlighting the navigation menu of whoever displays a higher percentage between the two elements.

let observer = new IntersectionObserver(function (entries) {
    (entry => {
        if ( &&  > 0.65) {
            
        }
    })
}, {
    threshold: [0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]
})

This is set herethreshold: [0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], when the proportion of target elements appearing in the detection area reaches20%,30%,40%,50%,60%,70%,80% The callback function is executed when the target element is visible and displayed in the detection area at a rate of65% when highlighting the navigation menu. This works better:

image

Check out this full example hereIntersectionObserver + scrollIntoView for elevator navigation.

Of course, it depends on the actual element block size and business requirements.

If it helps, help out with the kudos, thanks~