├── .pr-preview.json ├── w3c.json ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── compile.sh ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── auto-publish.yml ├── Makefile ├── README.md ├── CONTRIBUTING.md ├── explainer.md └── index.bs /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed", 4 | "params": { 5 | "force": 1 6 | } 7 | } -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [ 3 | "32061" 4 | ], 5 | "contacts": [ 6 | "svgeesus" 7 | ], 8 | "shortName": "IntersectionObserver", 9 | "repo-type": "rec-track" 10 | } 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors under the 2 | [W3C Software and Document 3 | License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). Contributions to 4 | Specifications are made under the [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 5 | 6 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e # Exit with nonzero exit code if anything fails 3 | 4 | bikeshed spec 5 | 6 | outdir=out 7 | if [ -n "$1" ]; then 8 | outdir="$1" 9 | fi 10 | 11 | if [ -d "${outdir}" ]; then 12 | echo Copy the generated spec into "${outdir}/index.html" 13 | cp index.html "${outdir}/index.html" 14 | fi 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes #??? 2 | 3 | The following tasks have been completed: 4 | 5 | * [ ] Modified Web platform tests (link to pull request) 6 | 7 | Implementation commitment: 8 | 9 | * [ ] WebKit (https://bugs.webkit.org/show_bug.cgi?id=) 10 | * [ ] Chromium (https://bugs.chromium.org/p/chromium/issues/detail?id=) 11 | * [ ] Gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=) 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash -o pipefail 2 | .PHONY: local remote deploy 3 | 4 | remote: index.bs 5 | @ (HTTP_STATUS=$$(curl https://api.csswg.org/bikeshed/ \ 6 | --output index.html \ 7 | --write-out "%{http_code}" \ 8 | --header "Accept: text/plain, text/html" \ 9 | -F die-on=warning \ 10 | -F md-Text-Macro="COMMIT-SHA LOCAL COPY" \ 11 | -F file=@index.bs) && \ 12 | [[ "$$HTTP_STATUS" -eq "200" ]]) || ( \ 13 | echo ""; cat index.html; echo ""; \ 14 | rm -f index.html; \ 15 | exit 22 \ 16 | ); 17 | 18 | local: index.bs 19 | bikeshed spec index.bs index.html --md-Text-Macro="COMMIT-SHA LOCAL COPY" 20 | -------------------------------------------------------------------------------- /.github/workflows/auto-publish.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: {} 4 | push: 5 | branches: [main] 6 | permissions: 7 | contents: read 8 | jobs: 9 | main: 10 | name: Build, Validate, Deploy 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: w3c/spec-prod@v2 17 | with: 18 | GH_PAGES_BRANCH: gh-pages 19 | TOOLCHAIN: bikeshed 20 | BUILD_FAIL_ON: "link-error" 21 | W3C_NOTIFICATIONS_CC: ${{ secrets.CC }} 22 | W3C_ECHIDNA_TOKEN: ${{ secrets.ECHIDNA_TOKEN }} 23 | W3C_WG_DECISION_URL: https://lists.w3.org/Archives/Public/public-webapps/2014JulSep/0627.html 24 | W3C_BUILD_OVERRIDE: | 25 | status: WD 26 | shortname: intersection-observer 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intersection Observers 2 | 3 | Implementation status: 4 | - Chromium: [Shipped in Chrome 51](https://www.chromestatus.com/feature/5695342691483648) 5 | - Edge: [Shipped in build 14986](https://developer.microsoft.com/en-us/microsoft-edge/platform/status/intersectionobserver/) 6 | - Firefox: [Shipped in Firefox 55](https://platform-status.mozilla.org/#intersection-observer) 7 | - WebKit: [Shipped in Safari 12.1 and iOS 12.2](https://developer.apple.com/documentation/safari_release_notes/safari_12_1_release_notes#3130314) 8 | 9 | [Explainer Doc](./explainer.md) 10 | 11 | [Draft Spec](https://w3c.github.io/IntersectionObserver/) 12 | 13 | Given that multiple native implementations of IntersectionObserver exist today, the polyfill that 14 | was formerly maintained in this repo is no longer supported and has been archived at https://github.com/GoogleChromeLabs/intersection-observer. 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Platform Working Group 2 | 3 | Contributions to this repository are intended to become part of Recommendation-track documents 4 | governed by the [W3C Patent Policy](http://www.w3.org/Consortium/Patent-Policy-20040205/) and 5 | [Document License](http://www.w3.org/Consortium/Legal/copyright-documents). To contribute, you must 6 | either participate in the relevant W3C Working Group or make a non-member patent licensing 7 | commitment. 8 | 9 | If you are not the sole contributor to a contribution (pull request), please identify all 10 | contributors in the pull request's body or in subsequent comments. 11 | 12 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 13 | 14 | ``` 15 | +@github_username 16 | ``` 17 | 18 | If you added a contributor by mistake, you can remove them in a comment with: 19 | 20 | ``` 21 | -@github_username 22 | ``` 23 | 24 | If you are making a pull request on behalf of someone else but you had no part in designing the 25 | feature, you can remove yourself with the above syntax. 26 | 27 | # Tests 28 | 29 | For normative changes, a corresponding 30 | [web-platform-tests](https://github.com/web-platform-tests/wpt) PR is highly appreciated. Typically, 31 | both PRs will be merged at the same time. Note that a test change that contradicts the spec should 32 | not be merged before the corresponding spec change. If testing is not practical, please explain why 33 | and if appropriate [file an issue](https://github.com/web-platform-tests/wpt/issues/new) to follow 34 | up later. Add the `type:untestable` or `type:missing-coverage` label as appropriate. 35 | -------------------------------------------------------------------------------- /explainer.md: -------------------------------------------------------------------------------- 1 | # Intersection Observers Explained 2 | 3 | ## What's All This About? 4 | 5 | This repo outlines an API that can be used to understand movement of DOM elements relative to another element or the browser top level viewport. Changes are delivered asynchronously and are useful for understanding the visibility of elements, managing pre-loading of DOM and data, as well as deferred loading of "below the fold" page content. 6 | 7 | ## Observing Position 8 | 9 | The web's traditional position calculation mechanisms rely on explicit queries of DOM state. Some of these are known to cause style recalculation and layout and, frequently, are redundant thanks to the requirement that scripts poll for this information. 10 | 11 | A body of common practice has evolved that relies on these behaviors, however, including (but not limited to): 12 | 13 | * Observing the location of "below the fold" sections of content in order to lazy-load content. 14 | * Implementing data-bound high-performance scrolling lists which load and render subsets of data sets. These lists are a central mobile interaction idiom. 15 | * Calculating element visibility. In particular, [ad networks now require reporting of ad "visibility" for monetizing impressions](https://www.iab.com/news/viewability-has-arrived-what-you-need-to-know-to-see-through-this-sea-change/). This has led to many sites abusing scroll handlers, [synchronous layout invoking readbacks](https://gist.github.com/paulirish/5d52fb081b3570c81e3a), and resorting to exotic plugin-based solutions for computing "true" element visibility (as a fraction of the element's intended size). 16 | 17 | These use-cases have several common properties: 18 | 19 | 1. They can be represented as passive "queries" about the state of individual elements with respect to some other element (or the global viewport). 20 | 1. They do not impose hard latency requirements; that is to say, the information can be delayed somewhat asynchronously (e.g. from another thread) without penalty. 21 | 1. They are poorly supported by nearly all combinations of existing web platform features, requiring extraordinary developer effort despite their widespread use. 22 | 23 | A notable non-goal is pixel-accurate information about what was actually displayed (which can be quite difficult to obtain efficiently in certain browser architectures in the face of filters, webgl, and other features). In all of these scenarios the information is useful even when delivered at a slight delay and without perfect compositing-result data. 24 | 25 | Given the opportunity to reduce CPU use, increase battery life, and eliminate jank it seems like a new API to simplify answering these queries is a prudent addition to the web platform. 26 | 27 | ### Proposed API 28 | 29 | ```js 30 | function callback(entries) { 31 | entries.forEach(function(entry) { 32 | if (entry.isIntersecting) 33 | doSomething(); 34 | }); 35 | }; 36 | var root = document.getElementById('root'); 37 | var target = document.getElementById('target'); 38 | var options_dict = { 39 | thresholds: [0.0, 0.3, 0.7, 1.0], 40 | root: root 41 | }; 42 | var observer = new IntersectionObserver(callback, options_dict); 43 | observer.observe(target); 44 | ``` 45 | 46 | The expected use of this API is that you create an IntersectionObserver with a root element; then observe one or more elements that are descendants of the root. The callback will be fired whenever any of the observed elements' ratio of (area of observed element's intersection with root / total area of observed element) crosses any of the observer's thresholds (i.e., transitions from less than the threshold to greater, or vice versa). The callback includes change records for all observed elements for which a threshold crossing has occurred since the last callback. 47 | 48 | ## Element Visibility 49 | 50 | The information provided by this API, combined with the default viewport query, allows a developer to easily understand when an element comes into, or passes out of, view. Here's how one might implement the IAB's "50% visible for more than a continuous second" policy for counting an ad impression: 51 | 52 | ```html 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | ```js 60 | // ads.js 61 | 62 | function logImpressionToServer() { /* ... */ } 63 | 64 | function isVisible(boundingClientRect, intersectionRect) { 65 | return ((intersectionRect.width * intersectionRect.height) / 66 | (boundingClientRect.width * boundingClientRect.height) >= 0.5); 67 | } 68 | 69 | function visibleTimerCallback(element, observer) { 70 | delete element.visibleTimeout; 71 | // Process any pending observations 72 | processChanges(observer.takeRecords()); 73 | if ('isVisible' in element) { 74 | delete element.isVisible; 75 | logImpressionToServer(); 76 | observer.unobserve(element); 77 | } 78 | } 79 | 80 | function processChanges(changes) { 81 | changes.forEach(function(changeRecord) { 82 | var element = changeRecord.target; 83 | element.isVisible = isVisible(changeRecord.boundingClientRect, changeRecord.intersectionRect); 84 | if ('isVisible' in element) { 85 | // Transitioned from hidden to visible 86 | element.visibleTimeout = setTimeout(visibleTimerCallback, 1000, element, observer); 87 | } else { 88 | // Transitioned from visible to hidden 89 | if ('visibleTimeout' in element) { 90 | clearTimeout(element.visibleTimeout); 91 | delete element.visibleTimeout; 92 | } 93 | } 94 | }); 95 | } 96 | 97 | var observer = new IntersectionObserver( 98 | processChanges, 99 | { threshold: [0.5] } 100 | ); 101 | 102 | var theAd = document.querySelector('#theAd'); 103 | observer.observe(theAd); 104 | ``` 105 | 106 | If more granular information about visibility is needed, the above code may be modified to use a sequence of threshold values. This higher rate of delivery might seem expensive at first glance, but note the power and performance advantages over current practice: 107 | 108 | - No scroll handlers need be installed/run (a frequent source of jank). 109 | - Off-screen ads do not deliver any events or set any timers until they come into view. 110 | - No polling, synchronous layouts, or plugins are required; only a single timeout to record the completed ad impression. 111 | 112 | ## Data Scrollers 113 | 114 | Many systems use data-bound lists which manage their in-view contents, recycling DOM to remain memory and layout-efficient while triggering loading of data that will be needed at some point in the near future. 115 | 116 | These systems frequently want to use different queries on the same scroll-containing viewport. Data loading can take a long time, so it is advantageous to pre-populate data stores with significantly more information than is visible. The rendered element count may display a much smaller subset of available data; only the "skirt" on each side of a scrolling area necessary to keep up with scrolling velocity (to avoid "blank" or "checkerboard" data). 117 | 118 | We can use an `IntersectionObserver` on child elements of a parent scrolling element to inform the system when to load data and recycle scrolled-out-of-view elements and stamp new content into them for rendering at the "end" of the list: 119 | 120 | ```html 121 | 145 | 146 |
2 | Title: Intersection Observer
3 | Status: ED
4 | ED: https://w3c.github.io/IntersectionObserver/
5 | Shortname: intersection-observer
6 | TR: https://www.w3.org/TR/intersection-observer/
7 | Previous version: from biblio
8 | Level: none
9 | Editor: Stefan Zager, Google, szager@google.com, w3cid 91208
10 | Editor: Emilio Cobos Álvarez , Mozilla, emilio@mozilla.com, w3cid 106537
11 | Editor: Traian Captan, Google, tcaptan@google.com, w3cid 137959
12 | Former Editor: Michael Blain, Google, mpb@google.com, w3cid 73819
13 | Abstract: This specification describes an API that can be used to understand the visibility and position of DOM elements ("targets") relative to a containing element or to the top-level viewport ("root"). The position is delivered asynchronously and is useful for understanding the visibility of elements and implementing pre-loading and deferred loading of DOM content.
14 | Group: webapps
15 | Repository: W3C/IntersectionObserver
16 | Test Suite: http://w3c-test.org/intersection-observer/
17 | Ignored Vars: rootMargin, docs, now
18 | Markup Shorthands: markdown yes
19 |
20 |
21 | 22 | urlPrefix: https://dom.spec.whatwg.org/ 23 | url: #node-trees; type: dfn; text: node tree 24 | urlPrefix: http://www.w3.org/TR/hr-time/ 25 | type: typedef; text: DOMHighResTimeStamp 26 | type: dfn; text: time origin 27 | urlPrefix: https://html.spec.whatwg.org/multipage/ 28 | urlPrefix: dom.html 29 | url: #document; type:dfn; text: Document 30 | urlPrefix: browsers.html 31 | type: dfn; text: browsing context 32 | type: dfn; text: top-level browsing context 33 | type: dfn; text: nested browsing context 34 | type: dfn; text: list of the descendant browsing contexts 35 | type: dfn; text: browsing context container 36 | type: dfn; text: fully active 37 | urlPrefix: webappapis.html; 38 | type: dfn; text: report the exception 39 | type: dfn; text: event loop 40 | type: dfn; text: queue a task 41 | type: dfn; text: run the fullscreen rendering steps 42 | type: dfn; text: run the animation frame callbacks 43 | url: #event-loop-processing-model; type: dfn; text: HTML Event Loops Processing Model 44 | type: dfn; text: relevant settings object 45 | url: #concept-environment-top-level-origin; type: dfn; text: top-level origin 46 | urlPrefix: infrastructure.html; 47 | url: #dfn-callback-this-value; type: dfn; text: callback this value 48 | urlPrefix: origin.html 49 | type: dfn; text: origin 50 | urlPrefix: https://heycam.github.io/webidl/ 51 | url: #dfn-simple-exception; type:exception; 52 | text: RangeError 53 | text: TypeError 54 | text: SyntaxError 55 | urlPrefix: #dfn-; type:dfn; text: throw 56 | urlPrefix: #idl-; type:interface; text: double 57 | urlPrefix: #idl-; type:interface; text: undefined 58 | url: #hierarchyrequesterror; type: exception; text: HierarchyRequestError 59 | urlPrefix: https://drafts.csswg.org/css-box/ 60 | url: #containing-block; type: dfn; text: containing block 61 | url: #padding-area; type: dfn; text: padding area 62 | url: #padding-edge; type: dfn; text: padding edge 63 | urlPrefix: https://drafts.csswg.org/css-display/ 64 | url: #containing-block-chain; type: dfn; text: containing block chain 65 | urlPrefix: http://www.w3.org/TR/css-masking-1/ 66 | url: #propdef-clip-path; type:dfn; text: clip-path 67 | urlPrefix: https://drafts.csswg.org/css-overflow-3/ 68 | url: #ink-overflow-rectangle; type:dfn; text: ink overflow rectangle 69 | url: #ink-overflow-region; type:dfn; text: ink overflow region 70 | url: #overflow-properties; type:dfn; text: overflow properties 71 | urlPrefix: https://drafts.csswg.org/css-transforms-1/ 72 | url: #transformation-matrix; type:dfn; text: transformation matrix 73 | url: #serialization-of-the-computed-value; type:dfn; text: serialization 74 | url: #identity-transform-function; type:dfn; text: identity transform function 75 | url: #post-multiplied; type:dfn; text: post-multiplied 76 | urlPrefix: https://drafts.csswg.org/cssom-view-1/ 77 | url: #pinch-zoom; type:dfn; text: pinch zoom 78 | urlPrefix: https://drafts.csswg.org/css2/visuren.html 79 | url: #viewport; type:dfn; text: viewport 80 | urlPrefix: https://drafts.fxtf.org/filter-effects/ 81 | url: #funcdef-filter-blur; type:dfn; text: blur 82 |83 | 84 |
85 | spec: css-values-3; type: dfn 86 | text: absolute length 87 | text: dimension 88 | spec:html; type:attribute; text:document 89 |90 | 91 |
147 | var observer = new IntersectionObserver(changes => {
148 | for (const change of changes) {
149 | console.log(change.time); // Timestamp when the change occurred
150 | console.log(change.rootBounds); // Unclipped area of root
151 | console.log(change.boundingClientRect); // target.getBoundingClientRect()
152 | console.log(change.intersectionRect); // boundingClientRect, clipped by its containing block ancestors, and intersected with rootBounds
153 | console.log(change.intersectionRatio); // Ratio of intersectionRect area to boundingClientRect area
154 | console.log(change.target); // the Element target
155 | }
156 | }, {});
157 |
158 | // Watch for intersection events on a specific target Element.
159 | observer.observe(target);
160 |
161 | // Stop watching for intersection events on a specific target Element.
162 | observer.unobserve(target);
163 |
164 | // Stop observing threshold events on all target elements.
165 | observer.disconnect();
166 |
167 | 179 | callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer); 180 |181 | 182 | This callback will be invoked when there are changes to a target's 183 | intersection with the intersection root, as per the 184 | processing model. 185 | 186 |
null;
195 | otherwise, it is the top-level browsing context's {{document}} node,
196 | referred to as the implicit root.
197 |
198 | An {{IntersectionObserver}} with a non-null {{IntersectionObserver/root}}
199 | is referred to as an explicit root observer,
200 | and it can observe any target {{Element}} that is a descendant of the
201 | {{IntersectionObserver/root}} in the containing block chain.
202 | An {{IntersectionObserver}} with a null {{IntersectionObserver/root}}
203 | is referred to as an implicit root observer.
204 | Valid targets for an implicit root observer include
205 | any {{Element}} in the top-level browsing context,
206 | as well as any {{Element}} in any nested browsing context
207 | which is in the list of the descendant browsing contexts of the top-level browsing context.
208 |
209 | When dealing with implicit root observers, the API makes a distinction between
210 | a target whose relevant settings object's origin is
211 | same origin-domain with the top-level origin, referred to as a
212 | same-origin-domain target;
213 | as opposed to a cross-origin-domain target.
214 | Any target of an explicit root observer is also a same-origin-domain target,
215 | since the target must be in the same document as the
216 | intersection root.
217 |
218 | Note: In {{MutationObserver}}, the {{MutationObserverInit}} options are passed
219 | to {{MutationObserver/observe()}} while in {{IntersectionObserver}} they are
220 | passed to the constructor. This is because for MutationObserver, each {{Node}}
221 | being observed could have a different set of attributes to filter for. For
222 | {{IntersectionObserver}}, developers may choose to use a single observer to
223 | track multiple targets using the same set of options; or they may use a different
224 | observer for each tracked target.
225 | {{IntersectionObserverInit/rootMargin}} or {{threshold}} values for each
226 | target seems to introduce more complexity without solving additional
227 | use-cases. Per-{{observe()}} options could be provided in the future if the need arises.
228 |
229 |
230 | [Exposed=Window]
231 | interface IntersectionObserver {
232 | constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
233 | readonly attribute (Element or Document)? root;
234 | readonly attribute DOMString rootMargin;
235 | readonly attribute DOMString scrollMargin;
236 | readonly attribute FrozenArray<double> thresholds;
237 | readonly attribute long delay;
238 | readonly attribute boolean trackVisibility;
239 | undefined observe(Element target);
240 | undefined unobserve(Element target);
241 | undefined disconnect();
242 | sequence<IntersectionObserverEntry> takeRecords();
243 | };
244 |
245 |
246 | null if none was provided.
285 | : rootMargin
286 | ::
287 | Offsets applied to the root intersection rectangle,
288 | effectively growing or shrinking the box that is used to calculate intersections.
289 | These offsets are only applied when handling same-origin-domain targets;
290 | for cross-origin-domain targets they are ignored.
291 |
292 | On getting, return the result of serializing the elements of {{[[rootMargin]]}}
293 | space-separated, where pixel lengths serialize as the numeric value followed by "px",
294 | and percentages serialize as the numeric value followed by "%". Note that
295 | this is not guaranteed to be identical to the |options|.{{IntersectionObserverInit/rootMargin}}
296 | passed to the {{IntersectionObserver}} constructor. If no
297 | {{IntersectionObserverInit/rootMargin}} was passed to the {{IntersectionObserver}}
298 | constructor, the value of this attribute is "0px 0px 0px 0px".
299 | : scrollMargin
300 | ::
301 | Offsets are applied to scrollports on the path from intersection root to target,
302 | effectively growing or shrinking the clip rects used to calculate intersections.
303 |
304 | On getting, return the result of serializing the elements of {{[[scrollMargin]]}}
305 | space-separated, where pixel lengths serialize as the numeric value followed by "px",
306 | and percentages serialize as the numeric value followed by "%". Note that
307 | this is not guaranteed to be identical to the |options|.{{IntersectionObserverInit/scrollMargin}}
308 | passed to the {{IntersectionObserver}} constructor. If no
309 | {{IntersectionObserverInit/scrollMargin}} was passed to the {{IntersectionObserver}}
310 | constructor, the value of this attribute is "0px 0px 0px 0px".
311 | : thresholds
312 | ::
313 | A list of thresholds, sorted in increasing numeric order,
314 | where each threshold is a ratio of intersection area to bounding box area
315 | of an observed target. Notifications for a target are generated when any
316 | of the thresholds are crossed for that target.
317 | If no |options|.{{IntersectionObserverInit/threshold}} was provided to the
318 | {{IntersectionObserver}} constructor, or the sequence is empty, the value
319 | of this attribute will be [0].
320 | : delay
321 | ::
322 | A number indicating the minimum delay in milliseconds
323 | between notifications from this observer for a given target.
324 | : trackVisibility
325 | ::
326 | A boolean indicating whether this {{IntersectionObserver}} will track
327 | changes in a target's visibility.
328 |
415 | [Exposed=Window]
416 | interface IntersectionObserverEntry {
417 | constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
418 | readonly attribute DOMHighResTimeStamp time;
419 | readonly attribute DOMRectReadOnly? rootBounds;
420 | readonly attribute DOMRectReadOnly boundingClientRect;
421 | readonly attribute DOMRectReadOnly intersectionRect;
422 | readonly attribute boolean isIntersecting;
423 | readonly attribute boolean isVisible;
424 | readonly attribute double intersectionRatio;
425 | readonly attribute Element target;
426 | };
427 |
428 | dictionary IntersectionObserverEntryInit {
429 | required DOMHighResTimeStamp time;
430 | required DOMRectInit? rootBounds;
431 | required DOMRectInit boundingClientRect;
432 | required DOMRectInit intersectionRect;
433 | required boolean isIntersecting;
434 | required boolean isVisible;
435 | required double intersectionRatio;
436 | required Element target;
437 | };
438 |
439 |
440 | null.
478 | Note that if the target is in a different browsing context than the intersection root,
479 | this will be in a different coordinate system
480 | than {{IntersectionObserverEntry/boundingClientRect}} and {{IntersectionObserverEntry/intersectionRect}}.
481 | : target
482 | ::
483 | The {{Element}} whose intersection with the
484 | intersection root changed.
485 | : time
486 | ::
487 | The attribute must return a {{DOMHighResTimeStamp}}
488 | that corresponds to the time the intersection was recorded, relative to the
489 | time origin of the global object associated with the IntersectionObserver instance
490 | that generated the notification.
491 |
497 | dictionary IntersectionObserverInit {
498 | (Element or Document)? root = null;
499 | DOMString rootMargin = "0px";
500 | DOMString scrollMargin = "0px";
501 | (double or sequence<double>) threshold = 0;
502 | long delay = 0;
503 | boolean trackVisibility = false;
504 | };
505 |
506 |
507 | 519 | "5px" // all margins set to 5px 520 | "5px 10px" // top & bottom = 5px, right & left = 10px 521 | "-10px 5px 8px" // top = -10px, right & left = 5px, bottom = 8px 522 | "-10px -5px 5px 8px" // top = -10px, right = -5px, bottom = 5px, left = 8px 523 |524 | : scrollMargin 525 | :: 526 | Similar to {{IntersectionObserverInit/rootMargin}}, 527 | this is a string of 1-4 components, 528 | each either an absolute length or a percentage. 529 | 530 | See {{IntersectionObserverInit/rootMargin}} above for the example. 531 | : threshold 532 | :: 533 | List of threshold(s) at which to trigger callback. 534 | callback will be invoked when intersectionRect's area changes from 535 | greater than or equal to any threshold to less than that threshold, 536 | and vice versa. 537 | 538 | Threshold values must be in the range of [0, 1.0] and represent a 539 | percentage of the area of the rectangle produced 540 | by getting the bounding box for target. 541 | 542 | Note: 0.0 is effectively "any non-zero number of pixels". 543 | : delay 544 | :: 545 | A number specifying the minimum delay in milliseconds 546 | between notifications from the observer for a given target. 547 | : trackVisibility 548 | :: 549 | A boolean indicating whether the observer should track visibility. 550 | Note that tracking visibility is likely to be a more expensive operation 551 | than tracking intersections. It is recommended that this option be used 552 | only when necessary. 553 |
0 to |thresholds|.
637 | 9. The {{IntersectionObserver/thresholds}} attribute getter will return
638 | this sorted |thresholds| list.
639 | 10. Let |delay| be the value of |options|.{{IntersectionObserverInit/delay}}.
640 | 11. If |options|.{{IntersectionObserverInit/trackVisibility}} is true
641 | and |delay| is less than 100, set |delay| to 100.
642 | 11. Set |this|'s internal {{[[delay]]}} slot to |options|.{{IntersectionObserverInit/delay}} to |delay|.
643 | 12. Set |this|'s internal {{[[trackVisibility]]}} slot to |options|.{{IntersectionObserverInit/trackVisibility}}.
644 | 13. Return |this|.
645 |
646 | -1,
657 | a {{IntersectionObserverRegistration/previousIsIntersecting}} property set to false,
658 | and a {{IntersectionObserverRegistration/previousIsVisible}} property set to false.
659 | 3. Append |intersectionObserverRegistration|
660 | to |target|'s internal {{[[RegisteredIntersectionObservers]]}} slot.
661 | 4. Add |target| to |observer|'s internal {{[[ObservationTargets]]}} slot.
662 |
663 | (|time| - |registration|.{{IntersectionObserverRegistration/lastUpdateTime}} < |observer|.{{IntersectionObserver/delay}}), skip further processing for |target|.
794 | 3. Set |registration|.{{IntersectionObserverRegistration/lastUpdateTime}} to |time|.
795 | 4. Let:
796 | - |thresholdIndex| be 0.
797 | - |isIntersecting| be false.
798 | - |targetRect| be a {{DOMRectReadOnly}} with |x|, |y|, |width|, and |height| set to 0.
799 | - |intersectionRect| be a {{DOMRectReadOnly}} with |x|, |y|, |width|, and |height| set to 0.
800 | 5. If the intersection root is not the implicit root,
801 | and |target| is not in the same {{document}} as the intersection root,
802 | skip to step 11.
803 | 6. If the intersection root is an {{Element}},
804 | and |target| is not a descendant of the intersection root
805 | in the containing block chain, skip to step 11.
806 | 7. Set |targetRect| to the {{DOMRectReadOnly}} obtained by getting the bounding box for
807 | |target|.
808 | 8. Let |intersectionRect| be the result of running the compute the intersection
809 | algorithm on |target| and |observer|'s intersection root.
810 | 9. Let |targetArea| be |targetRect|'s area.
811 | 10. Let |intersectionArea| be |intersectionRect|'s area.
812 | 11. Let |isIntersecting| be true if |targetRect| and |rootBounds| intersect or are edge-adjacent,
813 | even if the intersection has zero area (because |rootBounds| or |targetRect| have
814 | zero area).
815 | 12. If |targetArea| is non-zero, let |intersectionRatio| be |intersectionArea| divided by |targetArea|.1 if |isIntersecting| is true, or 0 if |isIntersecting| is false.
817 | 13. Set |thresholdIndex| to the index of the first entry in |observer|.{{thresholds}} whose value is greater than |intersectionRatio|, or the length of |observer|.{{thresholds}} if |intersectionRatio| is greater than or equal to the last entry in |observer|.{{thresholds}}.
818 | 14. Let |isVisible| be the result of running the visibility algorithm on |target|.
819 | 15. Let |previousThresholdIndex| be the |registration|'s
820 | {{IntersectionObserverRegistration/previousThresholdIndex}} property.
821 | 16. Let |previousIsIntersecting| be the |registration|'s
822 | {{IntersectionObserverRegistration/previousIsIntersecting}} property.
823 | 17. Let |previousIsVisible| be the |registration|'s
824 | {{IntersectionObserverRegistration/previousIsVisible}} property.
825 | 18. If |thresholdIndex| does not equal |previousThresholdIndex|,
826 | or if |isIntersecting| does not equal |previousIsIntersecting|,
827 | or if |isVisible| does not equal |previousIsVisible|,
828 | queue an IntersectionObserverEntry,
829 | passing in |observer|, |time|, |rootBounds|,
830 | |targetRect|, |intersectionRect|, |isIntersecting|,
831 | |isVisible|, and |target|.
832 | 19. Assign |thresholdIndex| to |registration|'s
833 | {{IntersectionObserverRegistration/previousThresholdIndex}} property.
834 | 20. Assign |isIntersecting| to |registration|'s
835 | {{IntersectionObserverRegistration/previousIsIntersecting}} property.
836 | 21. Assign |isVisible| to |registration|'s
837 | {{IntersectionObserverRegistration/previousIsVisible}} property.
838 |
839 |