Intersectionobserver API detailed article _javascript tips

Source: Internet
Author: User
Tags constructor garbage collection instance method

Warm tip: This article is currently only available in Chrome 51 and above.

2016.11.1 additional, Firefox 52 has also been implemented.

2016.11.29 added, Firefox people worry about the current specification is not stable, the future is difficult to ensure backward compatibility, so disable the API, need to manually open the DOM. Intersectionobserver.enabled is only fine.

The Intersectionobserver API is used to monitor whether an element has scrolled into the viewable area of the browser window (viewport) or scrolled into the viewable area of one of its ancestor elements. Its main function is to implement delay loading and display statistics. Let's take a look at a video introduction:

Again to look at the name, the first word intersection is the intersection of the meaning, as a child in mathematics to learn:

But in a Web page, the elements are rectangular:

The second word observer is the meaning of the observer, and the observe (R) of the Mutationobserver and the dead Object.observe.

Here's a list of all the parameters, properties, and methods in this API:

Generating an observer instance with a constructor let
observer = new Intersectionobserver (entries, observer) => {
 // In the callback function, you can get information about the intersection that occurs at each intersection for a
 (let entry of entries) {
 console.log (entry.time)
 Console.log (entry.target )
 Console.log (entry.rootbounds)
 console.log (Entry.boundingclientrect
 Console.log ( Entry.intersectionrect)
 Console.log (entry.intersectionratio)
 }
}, {//constructor option
 Root:null,
 threshold: [0, 0.5, 1],
 rootmargin: "50px, 0px"
})

//Instance Properties
observer.root
Observer.rootmargin
observer.thresholds

//instance Method
Observer.observe ()
Observer.unobserve ()
observer.disconnect ()
observer.takerecords ()

They are described in detail in three sections:

Constructors

New Intersectionobserver (callback, options)

Callback is a required parameter, which is invoked by the browser when there is an intersection, which is described in detail, and the entire parameter object and its three properties are optional:

Root

The Intersectionobserver API's scenario is basically this: a scrollable element, called the root element, which has many descendant elements, and wants to determine whether one of its descendant elements has scrolled into its own viewable range. This root parameter is used to specify the root element, and the default value is null.

If its value is null, the root element is not a real element, but rather the browser window, which can be understood as window, but window is not an element (not even a node). All elements in the current window can be interpreted as descendants of the null root element, which can be observed.

The following demo demonstrates the use of the root element as null:

<div id= "Info" > I hid at the bottom of the page, please scroll down </div>
<div id= "target" ></div>

<style>
 # info {
 position:fixed;
 }

 #target {
 position:absolute;
 Top:calc (100VH + 500px);
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => {
 if (!) target.isintersecting) {
 info.textcontent = "I came out"
 target.isintersecting = True
 } else {
 Info.textcontent = "I hide at the bottom of the page, please scroll down"
 target.isintersecting = False
 }
 }, {
 root:null//null can be omitted when c25/>})

 Observer.observe (target)
</script>

Note that here I'm judging by adding a property called Isintersecting on target to see if it's coming in or out, why do you do it? Just ignore it, and there's a section that explains it.

The root element, in addition to being null, can also be an arbitrary ancestor element of the target element:

<div id= "root" >
 <div id= "info" > scroll down to see me </div>
 <div id= "target" ></div>
</div>

<style>
 #root {
 position:relative;
 width:200px;
 HEIGHT:100VH;
 margin:0 Auto;
 Overflow:scroll;
 border:1px solid #ccc;
 }
 
 #info {
 position:fixed;
 }
 
 #target {
 position:absolute;
 Top:calc (100VH + 500px);
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => {
 if (!) target.isintersecting) {
 info.textcontent = "I came out"
 target.isintersecting = True
 } else {
 info.textcontent = "Scroll down to see me"
 target.isintersecting = false}
 }, {
 root:root
 })

 Observer.observe (target)
</script>

It is important to note that if root is not NULL, then the intersecting area is not necessarily within the viewport, because the intersection of root and target may also occur below the viewport, as demonstrated by the following demo:

<div id= "root" >
 <div id= "Info" > slowly scroll down </div>
 <div id= "target" ></div>
</div>

<style>
 #root {
 position:relative;
 width:200px;
 Height:calc (100VH + 500px);
 margin:0 Auto;
 Overflow:scroll;
 border:1px solid #ccc;
 }
 
 #info {
 position:fixed;
 }
 
 #target {
 position:absolute;
 Top:calc (100VH + 1000px);
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => {
 if (!) target.isintersecting) {
 info.textcontent = "I intersect with root, but you still can't see"
 target.isintersecting = True
 } else { C30/>info.textcontent = "Slowly scrolling down"
 target.isintersecting = False
 }
 }, {
 root:root
 })

 Observer.observe (target)
</script>

To sum up: In this section we talk about two types of root elements, null and arbitrary ancestor elements, where null values represent the root element as the current window (the viewport).

Threshold

When the target element intersects the root element, dividing the intersection area by the area of the target element results in a value from 0 to 1 (0% to 100%):

The basic principle of the Intersectionobserver API is that the callback function is triggered when the target element intersects the root element as a percentage of the area of the target element arrives or crosses some of the specified thresholds. The threshold parameter is used to specify the threshold value, and the default value is 0, which means that the two elements just hit the callback. Valid thresholds can be any number within a 0 to 1 closed range, such as 0.5 that triggers a callback when the intersection area is half the area of the target element. And you can specify multiple thresholds, in the form of arrays, such as [0, 0.5, 1], which means that two rectangles begin to intersect, half crossed, and a full intersection that triggers a callback function at three times. If you pass an empty array, it will automatically insert 0 into [0], which is also equivalent to the default value of 0.

<

The following animation shows when the callback function is triggered when the threshold parameter is [0, 0.5, 1], when the page is scrolled down:

The callback is triggered not only when the target element moves from the viewport to the viewport, but also from the viewport to the viewport:

You can verify the above two animations in this demo:

You can verify the above two animations in this demo:

<div id= "Info" >
 slowly scroll down, intersect:
 <span id= "Times" >0</span>
</div>
<div id= "Target" ></div>

<style>
 #info {
 position:fixed;
 }
 
 #target {
 position:absolute;
 top:200%;
 width:100px;
 height:100px;
 background:red;
 margin-bottom:100px;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => {
 times.textcontent = + Times.textcontent + 1
 }, {
 threshold: [0, 0.5, 1]
 })

 Observer.observe (target)
</script >

The order of the numbers in the threshold array is not tough, for readability, it's best to write from small to large. If a specified critical value is less than 0 or greater than 1, the browser complains:

<script>
New Intersectionobserver (() => {}, {
 threshold:2//syntaxerror:failed to construct ' intersec tion ': Threshold values must be between 0 and 1.
})

Rootmagin

In the beginning, this article said that one of the main uses of this API is to implement deferred loading, so the real delay load will wait for the IMG tag or other types of target blocks into the viewport to perform loading action? Obviously, it's too late. We usually preload hundreds of pixels in advance, which is what Rootmargin is for. Rootmargin can add an imaginary margin to the root element to scale the real root element area. For example, when Root is null set Rootmargin: "100px", the actual root element rectangle four edges will be magnified 100px, like this:

The effect can be imagined that if the threshold is 0, the callback function is triggered prematurely when the target element is about 100px from the viewport (whichever direction). Considering the common page does not have the need for horizontal scrolling, the value of the Rootmargin parameter is generally "100px 0px", this form, that is, the left and right margin are generally 0px. Here is a demo with Intersectionobserver to realize the delay loading when the picture is 500px from the viewport:

<div id= "Info" > picture at the bottom of the page, still not loaded, please scroll down </div>


<style>
 #info {
 position:fixed
 }

 #img {
 position:absolute;
 top:300%;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => {
 Observer.unobserve ( IMG)
 info.textcontent = "Start loading pictures!" "
 img.src = Img.dataset.src
 }, {
 rootmargin: 500px 0px"
 })

 Observer.observe (IMG)
</script>

Note that although the value of Rootmargin and CSS margin the same format, but there are some limitations, Rootmargin can only use PX and percentages of two units, with other units will be an error, such as using EM:

<script>
New Intersectionobserver (() => {}, {
 rootmargin: ' 10em '//syntaxerror:failed to construct ' Intersection ': Rootmargin must to specified in pixels or percent.
})
</script>

Rootmargin percent is the percentage of the true size of the root element, such as rootmargin: "0px 0px 50% 0px", which means that the size of the root element is enlarged by 50%.

If the negative margin is used, the real root element area will be reduced, and the corresponding delay loading will be deferred, such as using the Rootmargin: " -100px", the target element scrolling into the root element of the visual region within 100px when the callback can be triggered.

Instance

Instance Properties

Root

The root element of the Observer instance (the default value is NULL):

New Intersectionobserver (() => {}). root//null
new Intersectionobserver (() => {}, {root:document.body}). root Document.body

Rootmargin

The serialized value of the Rootmargin parameter (the default value is "0px"):

New Intersectionobserver (() => {}). Rootmargin//"0px 0px 0px 0px"
new Intersectionobserver (() => {}, {Rootmargi N: "50px"}). Rootmargin//"50px 50px 50px 50px"
New Intersectionobserver (() => {}, {rootmargin: "50% 0px"}). Rootmar Gin//"50% 0px 50% 0px"
New Intersectionobserver (() => {}, {rootmargin: "50% 0px 50px"}). Rootmargin//50% 0px 50p x 0px " 
New Intersectionobserver (() => {}, {rootmargin:" 1px 2px 3px 4px}). Rootmargin//"1px 2px 3px 4px"

Thresholds

Threshold parameter (the default is 0) after the serialized value, even if you pass in a number, serialization is also a number of groups, the current implementation of the Chrome digital precision will be lost, but it does not hinder:

New Intersectionobserver (() => {}). Thresholds//[0]
New Intersectionobserver (() => {}, {threshold:1}). Thresholds//[1]
new Intersectionobserver (() => {}, {threshold: [0.3, 0.6]}). thresholds//[[0.30000001192092896 , 0.6000000238418579]]
Object.isfrozen (New Intersectionobserver (() => {}). Thresholds)/True, is a freeze array

These three instance properties are used to identify an observer instance, and are both readable and not much used in the code.

Instance method

Observe ()

Observing a target element, an observer instance can observe any number of target elements. Note that there may be a reunion question: can delegate? Can you just call the observe method once to see all the IMG elements in a page, even those that are not generated? The answer is no, it's not an event, it's not bubbling.

Unobserve ()

By canceling the observation of a target element, deferred loading is usually one-time, and the observe callback should call the Unobserve () element directly.

Disconnect ()

Cancel observation of all observed target elements

Takerecords ()

Understanding this approach requires a little bit of the underlying thing: inside the browser, when an observer instance observes several intersecting actions at a time, it does not immediately execute the callback, and it calls Window.requestidlecallback () (Currently only Chrome Support) to execute our specified callback function asynchronously, and also to specify that the maximum latency is 100 milliseconds, which is equivalent to the browser executing:

Requestidlecallback (() => {
 if (Entries.length > 0) {
 callback (entries, observer)
 }}
, {
 Timeout:100
})

It is not certain that your callback may be executed in the next 1 milliseconds or in the 100th millisecond. At some point between this indeterminate 100 milliseconds, if you desperately need to know if the observer instance has observed the intersection, you have to call the Takerecords () method, which synchronizes the returns containing several intersectionobserverentry An array of objects (the Intersectionobserverentry object contains information for each intersection, in the next section), and if the observer instance does not observe the intersecting action at the moment, it returns an empty array.

Note that for the same intersection information, synchronous takerecords () and asynchronous callback functions are mutually exclusive, and if the callback executes first, then you manually call Takerecords () and you will inevitably get an empty array if you have passed Takerecords () If you get the Intersect, the callback you specify will not be executed (Entries.length > 0 is False).

The real use of this method is very small, I can not lift out, I could only write a verification of the above two paragraphs (irregular timing) of the test code:

<script>
 setinterval (() => {let
 observer = new Intersectionobserver (entries => {
 if ( Entries.length) {
 Document.body.innerHTML + = "<p> asynchronous Requestidlecallback () callback first executed"
 }
 })

 Requestanimationframe (() => {
 settimeout () => {
 if (observer.takerecords (). length) {
  Document.body.innerHTML + = "<p> synchronized takerecords () first executed"
 }
 }, 0)
 })

 Observer.observe ( document.body)

 scrollto (0, 1e10)
 },
</script>

callback function

New Intersectionobserver (function (entries, observer) {for
 (let entry of entries) {
 console.log (entry.time) C21/>console.log (Entry.target)
 console.log (entry.rootbounds)
 Console.log (entry.boundingclientrect
 Console.log (entry.intersectionrect)
 console.log (entry.intersectionratio)
 }
)

The callback function has two parameters, and the second parameter is the Observer instance itself, which is generally useless because the instance is usually assigned to a variable, and this is the instance in the callback function. The first parameter is an array that contains several Intersectionobserverentry objects, which is the same as the return value of the Takerecords () method. Each Intersectionobserverentry object represents an intersection, and its attributes contain the various messages that intersect. The order of the Intersectionobserverentry objects in the entries array is arranged in the order in which the target elements it belongs to were originally observe ().

Time

The number of milliseconds (with decimals) when the intersection occurs when the page is opened, that is, the return value of Performance.now () when the intersection occurs, such as 60000.560000000005, which is the intersection that occurs approximately 1 minutes after the page is opened. Subtracting this value with Performance.now () in the callback function calculates how many milliseconds the callback function is delayed by Requestidlecallback:

<script> LET
 observer = new Intersectionobserver ([entry]) => {
 document.body.textContent = ' intersection occurs in ${ Performance.now ()-entry.time} millisecond ago '
 })

 Observer.observe (document.documentelement)
</script>

You can keep refreshing the demo, which is up to 100 milliseconds, because the maximum latency that is set inside the browser is 100.

Target

The target element at which the intersection occurs, because a root element can observe multiple target elements, so this target is not necessarily what element.

Rootbounds

An object value that represents the rectangular information for the visible region of the root element when the intersection occurs, like this:

{
 "top": 0, "
 Bottom": $,
 "left": 0,
 "right": 1280,
 "width": 1280,
 "height":

Boundingclientrect

The rectangle information for the target element when the intersection occurs, equivalent to Target.getboundingclientrect ().

Intersectionrect

The rectangular information of the intersection area between the root element and the target element.

Intersectionratio

A value from 0 to 1 that represents the percentage of the intersection area as the target element region, which is the value of the area of the Intersectionrect divided by the area of the boundingclientrect.

The case of the welt is exceptional.

As has been said above, the basic principle of the Intersectionobserver API is to detect the change in the intersection rate. Each observer instance maintains a field for the last intersection rate (previousthreshold) for all target elements, and assigns an initial value to previousthreshold when executing observe () 0, and then each time a new intersection is detected that satisfies (arrives or crosses) a specified threshold in the thresholds, and that threshold differs from the current previousthreshold value, triggers the callback and assigns the new critical value that is satisfied to the Previousthreshold, it's easy to repeat, right.

But I don't know if you've noticed, as mentioned before, that when the target element is far from the root element and the root element is welt, at this time will also trigger the callback (if thresholds 0), but this and the principle of work is contradictory ah, away from the very far the intersection rate is 0, even if the welt, the intersection rate or 0, the value has not changed, The callback should not be triggered. True, this contradicts the basic principle of work, but this is a special case where the target element moves from far away from the root element to the root element, and it satisfies the critical value of 0, even if 0 equals 0.

There is also a special case in point where the target element moves from somewhere within the root element (the intersection rate is already 1) to the root element welt (or 1) and triggers the callback (if there are 1 in the thresholds).

The target element width or height of 0 is also a special case

Most of the time our target element is an empty img tag or an empty div container, if CSS is not set, the width and height of these elements is 0px, the rendering of the rectangular area is 0px2, that is the intersection rate will encounter divided by 0 of this mathematically illegal operation of the problem, even in JavaScript divided by 0 does not throw an exception or get Infinity, but the intersection rate is always Infinity means that the callback will never trigger, so this situation must be treated with special treatment.

The special treatment is that the 0-area target element's intersection rate is either 0 or 1. Either the welt or the inside of the root element, the intersection rate is 1, and the other is 0. 1 to 0 triggers the callback, and 0 to 1 triggers the callback, in both cases:

Because of this feature, it is meaningless to set a threshold value for a target element of 0 area, setting what value, setting a few, is an effect.

But note that the Intersectionratio attribute in the Intersect information is always 0, very burning, I know:

<div id= "target" ></div>

<script> let
 observer = new Intersectionobserver ([entry]) => {
 alert (entry.intersectionratio)
 })

 Observer.observe (target)
</script>

Observe () is the case that has been intersecting before?

I do not know whether you have this question, anyway I have. Observe () after a target element that already intersects the root element, does not scroll the page again, meaning that the intersection rate will never change, the callback should not occur, but it still happens. This is because when the observe () is executed, the browser initializes the Previousthreshold to 0 instead of initializing it to the current true intersection rate, and then detects that the intersection rate changes at the next intersection detection, so this is not a special treatment.

When does the browser detect intersection?

Our common display is 60hz, which means that the browser needs to draw 60 times per second (60fps), approximately every 16.667ms. If you use a 200hz monitor, the browser will be drawn once every 5ms. We refer to 16.667ms and 5ms, which each time the interval is drawn, called frame (frame, and the frame in HTML is not a thing). The browser's rendering work is in this frame, the following figure is the chrome in each frame of the browser to do (I added the original intersection observations phase):

It can be seen that the intersection detection (intersection observations) occurs after Paint composite, and how often the detection is based on the refresh rate of the display device. But to be sure, every time you draw a different picture, will be the intersection detection, there will be no slip through the screen.

One of the most critical values to arrive at once or across, select a recent

If an observer instance sets 11 critical values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1], how many times does a callback function trigger when the target element and the root element scroll from a completely disjoint state to an intersection rate of 1? The answer is: not sure. To see the rolling speed, if the scrolling speed is slow enough, the point at which each intersection rate reaches the next critical value occurs in a different frame (the browser draws at least 11 times), then 11 intersections are detected and the callback function is executed 11 times, if the scrolling speed is fast enough, Never intersecting to full intersection occurs in the same frame, the browser only draws once, although the browser knows that this one scrolling operation meets 11 specified thresholds (never intersect to 0, from 0 to 0.1, from 0.1 to 0.2). , but it only considers the nearest threshold, which is 1, and the callback function triggers only once:

<div id= "Info" > Intersection:
 <span id= "Times" >0</span>
 <button onclick= " Document.scrollingElement.scrollTop = 10000 "> scroll to the lowest part </button>
</div>
<div id=" Target " ></div>

<style>
 #info {
 position:fixed;
 }

 #target {
 position:absolute;
 top:200%;
 width:100px;
 height:100px;
 background:red;
 margin-bottom:100px;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => {
 times.textcontent = + Times.textcontent + 1
 }, {
 threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]//11 critical Value
 }) 
   observer.observe (target)
</script>

Leaving the viewport is also a truth, if the root and target elements of the intersection of the first from the full intersection into 0.45, and then from 0.45 to completely disjoint, then the callback function will only trigger two times.

How do you determine whether you are currently intersecting?

I've got a couple of demo demos that use a few lines of seemingly cumbersome code to determine if the target element is in the viewport:

if (!target.isintersecting) {
 //intersect
 target.isintersecting = True
} else {
 //do not want to pay
 Target.isintersecting = False
}

Why? Is it not possible to use the Entry.intersectionratio > 0 judgment:

<div id= "Info" > not visible, please scroll down very slowly </div>
<div id= "target" ></div>

<style>
 # info {
 position:fixed;
 }

 #target {
 position:absolute;
 top:200%;
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver ([entry]) => {
 if ( Entry.intersectionratio > 0) {
 //fast scrolling will execute to here
 info.textcontent = "visible"
 } else {
 //slow scrolling will execute here
 info.textcontent = "not visible, please scroll down very slowly
 }
 }

 " Observer.observe (target)
</script>

A cursory look, it seems to work, but don't forget about the welt, if you scroll slowly, when the top of the target element and the bottom of the viewport just close, the browser detects the intersection, the callback function triggers, but then entry.intersectionratio equals 0, will enter Else branch, continue to roll down, the callback function will not be triggered, the hint text has been stuck in the invisible state; but if you scroll fast, when the browser detects intersection, it has crossed the 0 threshold, there is the actual intersection area, Entry.intersectionratio > 0 is true, too. So writing can cause code execution to be unstable and not to be done.

Besides adding new attributes to the element to record whether the last callback was triggered or going out, I also thought of another way to set a very small threshold of close to 0 for the threshold option, such as 0.000001, and then use Entry.intersectionratio > 0, this will not be affected by the impact of the welt, and will not be affected by the rolling speed:

<div id= "Info" > not visible, scroll down at any speed </div>
<div id= "target" ></div>

<style>
 # info {
 position:fixed;
 }

 #target {
 position:absolute;
 top:200%;
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver ([entry]) => {
 if ( Entry.intersectionratio > 0) {
 info.textcontent = "visible"
 } else {
 info.textcontent = "not visible, scroll down at any Speed" c20/>}
 }, {
 threshold: [0.000001]
 })

 Observer.observe (target)
</script>

What happens if the target element is not a descendant element of the root element?

If the target element is not a descendant element of the root element while executing observe (), and the browser does not give an error, Chrome will warn the use of this usage since 53 (which I propose), reminding the developer that this usage may be wrong. Why not more stringent, direct error? Because the hierarchical relationships of elements can vary, someone might write this code:

<div id= "root" ></div>
<div id= "target" ></div>

<style>
 #target {
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => alert ("See Me"), {root:root}) C11/>observer.observe (target)//target is not a descendant element of Root at this time, the Chrome console warns: Target element is not a descendant of root.
 Root.appendchild (target)//Now is, trigger callback
</script>

Or the element that is observe is not added to the DOM tree at this time:

<div id= "root" ></div>

<style>
 #target {
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => alert ("See Me"), {root:root}) Let
 target = document.createelement ("div")//Not yet in the DOM tree
 observer.observe (target)/target is not the descendant element of Root at this time, Ch The Rome console warns: Target element is not a descendant of root.
 Root.appendchild (target)//Now is, trigger callback
</script>

That is, as long as the intersection occurs, the target element is the descendant element of the root element, and it can be done observe ().

The descendant element is not enough, the root element must be the ancestor containing block of the target element

The descendant element that requires the target element to be the root element is only said from the DOM structure. One easy to understand constraint, and another less easily understood restriction is that the root element rectangle must be the ancestor containing block of the target element rectangle (the containing block is also chained, like the prototype chain). For example, the following demo shows that two randomly moving elements a and b,a are the parent elements of B, but both of the position are fixed, causing a to be not a B-containing block, so this is an invalid observation operation, trying to change the fixed to relative to find back The modulation triggers:

&lt;div id= "A" &gt; &lt;div id= "b" &gt;&lt;/div&gt; &lt;/div&gt; &lt;div id= "info" &gt;0%&lt;/div&gt; &lt;style&gt; #a, #
 b {position:fixed/* Try to change to relative * * * width:200px;
 height:200px;
 opacity:0.8;
 #a {background:red} #b {Background:blue} #info {width:200px;
 margin:0 Auto;
 #info:: Before {content: "intersection Ratio:";  } &lt;/style&gt; &lt;script&gt; Let animate = (element, oldcoordinate = {x:0, y:0}) =&gt; {Let newcoordinate = {x: Math.random () * (Innerwidth-element.clientwidth), Y:math.random () * (innerheight-element.clientheight)} Let Keyf Rames = [Oldcoordinate, Newcoordinate].map (coordinatetolefttop) Let duration = Calcduration (Oldcoordinate, newcoordinate) Element.animate (keyframes, duration). OnFinish = () =&gt; animate (element, newcoordinate)} Let Coordin atetolefttop = Coordinate =&gt; ({left:coordinate.x + "px", Top:coordinate.y + "px"}) let Calcduration = (oldcoor Dinate, Newcoordinate) =&gt; {//move speedDegrees for 0.3 px/ms return Math.hypot (oldcoordinate.x-newcoordinate.x, OLDCOORDINATE.Y-NEWCOORDINATE.Y)/0.3} animate (A ) Animate (b) &lt;/script&gt; &lt;script&gt; Let thresholds = Array.from ({length:200}, (k, v) =&gt; v/200)//20  0 critical values correspond to 200px new Intersectionobserver ([entry]) =&gt; {info.textcontent = (Entry.intersectionratio *). toFixed (2) + "%"}, {root:a, threshold:thresholds}). Observe (b) &lt;/script&gt;

What happens when you delete a target element from the DOM tree?

Suppose that the root element and the target element are now intersecting, if you delete the target element or even the root element from the DOM tree, or if the target element is not a descendant element of the root element through a DOM manipulation, or by changing the CSS property, the root element is no longer the containing block of the target element, or the Display:none hides an element, which causes the intersection rate of both to suddenly change to 0, and the callback function can be triggered:

<div id= "Info" > Delete target elements also trigger callbacks
 <button onclick= "Document.body.removeChild (target)" > Delete target</ button>
</div>
<div id= "target" ></div>


<style>
 #info {
 position: fixed;
 }
 
 #target {
 position:absolute;
 top:100px;
 width:100px;
 height:100px;
 background:red;
 }
</style>

<script> let
 observer = new Intersectionobserver (() => {
 if (!) document.getElementById ("target")) {
 info.textcontent = "target was deleted"
 }
 })

 Observer.observe ( Target)
</script>

About IFRAME

Before the Intersectionobserver API, you can't tell whether an iframe page or an element in the page is in the viewport of the top-level window in a Cross-domain iframe page, which is why you should invent A very important reason for the Intersectionobserver API. Take a look at the image below to illustrate:

No matter how many layers of iframe, intersectionobserver can accurately determine whether the target element appears in the viewport of the top-level window, regardless of the cross-domain domain.

The previous mention of the root element null indicates that the actual root element is the viewport of the current window, now more specifically, it should be the viewport of the topmost window.

If the current page is an IFRAME page, and the top-level page is Cross-domain, and the callback is triggered when the root element is null, the Rootbounds property of the Intersectionobserverentry object you get will be null, even if two pages are not cross-domain, then Rootbounds properties of the rectangular coordinates of the system and Boundingclientrect and intersectionrect the two rectangles are also different, the former coordinate system of the original point is the upper left corner of the top window, the latter two are the current iframe window in the upper left corner.

Since advertising 90% on the internet is a cross-domain iframe, I think the intersectionobserver API can greatly simplify the implementation of delay loading and real exposure statistics for these ads.

The root element cannot be an element under another frame

If there is no cross-domain, can the root element be an ancestor element in the upper frame? Like the following:

<div id= "root" >
 <iframe id= "iframe" ></iframe>
</div>

<script>
 Let iframehtml = '
 <div id= ' target ' ></div>

 <style>
 #target {
 width:100px;
 height:100px;
 background:red;
 }
 </style>

 <script> let
 observer = new Intersectionobserver (() => {
 alert ("Intersecting" )
 }, {
 root:top.root
 })

 Observer.observe (target)
 <\/script> '

 iframe.src = Url.createobjecturl (New Blob ([iframehtml], {"type": "Text/html"})
</script>

I don't know if root in this demo is not the ancestor of target, but the specification clearly stipulates that the observation is invalid and the root element cannot be from another frame. The conclusion is that the root element is either null or an ancestor in the same frame contains a block element.

Are you really just judging two elements intersect?

The actual situation never looks so simple, the browser really just judge two rectangles intersect? Look at the following code:

<div id= "Parent" >
 <div id= "target" ></div>
</div>

<style>
 #parent {
 width:20px;
 height:20px;
 background:red;
 Overflow:hidden;
 }

 #target {
 width:100px;
 height:100px;
 Background:blue;
 }
</style>

<script> let
 observer = new Intersectionobserver ([entry]) => {
 alert (' Intersect rectangle is: ${entry.intersectionrect.width} x ${entry.intersectionrect.width} ')
 }

 Observer.observe (target)
</script>

This demo Reagan element for the current viewport, the target element is a 100x100 rectangle, if it is really to judge the intersection of two rectangles so simple, that the intersection rectangle should be x 100, but the coming out of the intersecting rectangle is x 20. Because of its actual intersection detection before there is a reduction of the target elements of the rectangle of the steps, cut down to and the root element to determine the intersection, the basic idea is to cut the target element by the "target elements and the existence of elements between the element" block out the block, the specific reduction steps are such (with rect Represents the final target element rectangle):

1, let rect as the target element rectangle
2, let the current as the target element of the parent element
3, if current is not the root element, proceed to the following loop:
If current's overflow is not visible (scroll or hidden or auto) or current is an IFRAME element (the IFrame is born with Overflow:auto), then:
Let rect equal the intersection of the Rect and current rectangles (to exclude the scroll bar area)
The parent element that lets current be current (the parent element of the HTML element in the IFRAME is the IFRAME element in the parent page)
That is, it is actually the process of iterating up and down the DOM tree of the target element. Looking at the demo above, the target element rectangle begins with a 100x100, then intersects its parent element to 20x20, then the BODY element and the HTML element are not set overflow, so the final intersection with the viewport is the 20x20 rectangle.

About double-pointing scaling

The mobile end device and the OS X system allow the user to enlarge a portion of the page with two fingers:

If a part of the page is magnified, it also means that certain areas of the page edge are displayed outside the viewport:

In these cases the Intersectionobserver API does not specialize, whether it is a root element or a target element, and their rectangles are the real dimensions before zooming (as the Getboundingclientrect () method shows), And even if the intersection really happens in areas where the user's eyes are not visible because of scaling, the callback function triggers. If you use a MAC system, you can now test any of the above demo.

About garbage collection

An observer instance is a weak reference for both the root element and the target element, just as Weakmap is a weak reference to its key. If the target element is garbage collected, the browser will no longer detect it, and if the root element is garbage collected, there is a problem, the root element is gone, but the observer instance is still there, and if so, which observer instance is used:

<div id= "root" ></div>
<div id= "target" ></div>

<script> let
 observer = new Intersectionobserver (() => {}, {root:root})//root element a total of two references, one is a reference in the DOM tree, one is a reference to global variable root
 document.body.remove Child (Root)//Remove root = null from the DOM tree
 settimeout (() => {
 GC ()//manual GC, which needs to be passed into--js-flag when launching Chrome s= '--expose-gc ' option
 console.log (observer.root)//null, the root element of the observer instance has been garbage collected
 Observer.observe (target)/ Uncaught Invalidstateerror:observe () called on a intersectionobserver with a invalid root, any method that executes the observer will have an error.
 })
</script>

That is to say, the observer instance is dead. This error is from Chrome 53 (I propose), 51 and 52 will only fail silently.

Background tab page

Because Chrome does not render background tabs, it does not detect intersections, and will not continue until you switch back and forth. You can use the Command/ctrl + left button to open the above arbitrary demo try.

Name of the Spit slot

Threshold and thresholds

The parameters of the constructor are called threshold, and the properties of the instance are called thresholds. I understand that the former can be a singular form of a number, but also a plural form of an array, so the singular form, and the latter serialized only to be a number of groups, so the plural is used. But unification is more important, I think the plural form is not a problem, I began to study the API when I tried to preach {thresholds: [1]}, tried to find out that more than one s, pit dead.

Disconnect

What the? Disconnect What do you mean? What's the Connect? I only know observe and unobserve, you he is called Unobserveall will die. The name is easily unknown and the result is a simple one. This is actually to unify with Mutationobserver and Performanceobserver.

Rootbounds & Boundingclientrect & Intersectionrect

These three are returned to a rectangular information, this is the same kind, but the name does not have a little rule, let a person can't remember. I suggest that call Rootrect & Targetrect & Intersectionrect, once again remember, really do not know how to write the norms of people how to think.

Polyfil

The person writing the specification will maintain a polyfill on the Github warehouse, which is not yet completed. However, Polyfill obviously can not support the detection of elements within the IFRAME, many details can not be simulated.

Other browsers to implement progress

firefox:https://bugzilla.mozilla.org/show_bug.cgi?id=1243846

safari:https://bugs.webkit.org/show_bug.cgi?id=159475

Edge:https://developer.microsoft.com/en-us/microsoft-edge/platform/status/intersectionobserver

Summarize

Although the API currently has a history of the specification, but still very imperfect, a large number of details are not specified; the implementation of the Chrome has been six months, but there are still a lot of bugs (mostly suspected bugs, after all, the specification is imperfect). Therefore, there are some details of this article I deliberately skipped, such as the target element is greater than the root element, or even the root element area of 0, support does not support SVG these, because I do not know what is the correct performance.

2016-8-2 Worm: Asked by colleagues today a real demand, "Statistics Taobao search page in the page opened two seconds after the display area of more than 50% baby", I immediately thought of using intersectionobserver:

SetTimeout (() => {let
 observer = new Intersectionobserver (entries => {
 Entries.foreach (entry => {
 Console.log (Entry.target)//Got the desired baby element
 })
 Observer.disconnect ()//statistics to no longer need to continue to observe
 }, {
 threshold: 0.5//As long as the area reached 50% of the baby element 
 }

 //Observe all the baby elements
 Array.from (Document.queryselectorall ("#mainsrp-itemlist. Item ). ForEach (Item => observer.observe (item)
}, 2000)

You don't have to do any math, it's just a blast, and of course, because of compatibility issues, this code can't be used.

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.