Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Intersection Observer Scroll Margin #1

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
SHELL=/bin/bash -o pipefail
.PHONY: local remote deploy

remote: index.bs
@ (HTTP_STATUS=$$(curl https://api.csswg.org/bikeshed/ \
--output index.html \
--write-out "%{http_code}" \
--header "Accept: text/plain, text/html" \
-F die-on=warning \
-F md-Text-Macro="COMMIT-SHA LOCAL COPY" \
-F file=@index.bs) && \
[[ "$$HTTP_STATUS" -eq "200" ]]) || ( \
echo ""; cat index.html; echo ""; \
rm -f index.html; \
exit 22 \
);

local: index.bs
bikeshed spec index.bs index.html --md-Text-Macro="COMMIT-SHA LOCAL COPY"
101 changes: 73 additions & 28 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ Previous version: from biblio
Level: none
Editor: Stefan Zager, Google, szager@google.com, w3cid 91208
Editor: Emilio Cobos Álvarez , Mozilla, emilio@mozilla.com, w3cid 106537
Editor: Traian Captan, Google, tcaptan@google.com, w3cid 137959
Former Editor: Michael Blain, Google, mpb@google.com, w3cid 73819
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.
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.
Group: webapps
Repository: W3C/IntersectionObserver
Test Suite: http://w3c-test.org/intersection-observer/
Expand Down Expand Up @@ -168,15 +169,15 @@ The IntersectionObserverCallback</h3>
callback IntersectionObserverCallback = undefined (sequence&lt;IntersectionObserverEntry> entries, IntersectionObserver observer);
</pre>

This callback will be invoked when there are changes to <a>target</a>'s
This callback will be invoked when there are changes to <a for="IntersectionObserver">target</a>'s
intersection with the <a>intersection root</a>, as per the
<a>processing model</a>.

<h3 id='intersection-observer-interface'>
The IntersectionObserver interface</h3>

The {{IntersectionObserver}} interface can be used to observe changes in the
intersection of an <a>intersection root</a> and one or more <a>target</a> {{Element}}s.
intersection of an <a>intersection root</a> and one or more <a for="IntersectionObserver">target</a> {{Element}}s.

The <dfn for="IntersectionObserver">intersection root</dfn>
for an {{IntersectionObserver}} is the value of its {{IntersectionObserver/root}} attribute
Expand All @@ -186,22 +187,22 @@ referred to as the <dfn for="IntersectionObserver">implicit root</dfn>.

An {{IntersectionObserver}} with a non-<code>null</code> {{IntersectionObserver/root}}
is referred to as an <dfn for="IntersectionObserver">explicit root observer</dfn>,
and it can observe any <a>target</a> {{Element}} that is a descendant of the
and it can observe any <a for="IntersectionObserver">target</a> {{Element}} that is a descendant of the
{{IntersectionObserver/root}} in the <a>containing block chain</a>.
An {{IntersectionObserver}} with a <code>null</code> {{IntersectionObserver/root}}
is referred to as an <dfn for="IntersectionObserver">implicit root observer</dfn>.
Valid <a>targets</a> for an <a>implicit root observer</a> include
Valid <a for="IntersectionObserver">targets</a> for an <a>implicit root observer</a> include
any {{Element}} in the <a>top-level browsing context</a>,
as well as any {{Element}} in any <a>nested browsing context</a>
which is in the <a>list of the descendant browsing contexts</a> of the <a>top-level browsing context</a>.

When dealing with <a>implicit root observers</a>, the API makes a distinction between
a <a>target</a> whose <a>relevant settings object</a>'s <a>origin</a> is
a <a for="IntersectionObserver">target</a> whose <a>relevant settings object</a>'s <a>origin</a> is
<a>same origin-domain</a> with the <a>top-level origin</a>, referred to as a
<dfn for="IntersectionObserver">same-origin-domain target</dfn>;
as opposed to a <dfn for="IntersectionObserver">cross-origin-domain target</dfn>.
Any <a>target</a> of an <a>explicit root observer</a> is also a <a>same-origin-domain target</a>,
since the <a>target</a> must be in the same <a>document</a> as the
Any <a for="IntersectionObserver">target</a> of an <a>explicit root observer</a> is also a <a>same-origin-domain target</a>,
since the <a for="IntersectionObserver">target</a> must be in the same <a>document</a> as the
<a>intersection root</a>.

Note: In {{MutationObserver}}, the {{MutationObserverInit}} options are passed
Expand All @@ -212,7 +213,7 @@ being observed could have a different set of attributes to filter for. For
track multiple targets using the same set of options; or they may use a different
observer for each tracked target.
{{IntersectionObserverInit/rootMargin}} or {{threshold}} values for each
<a>target</a> seems to introduce more complexity without solving additional
<a for="IntersectionObserver">target</a> seems to introduce more complexity without solving additional
use-cases. Per-{{observe()}} options could be provided in the future if the need arises.

<pre class="idl">
Expand All @@ -221,6 +222,7 @@ interface IntersectionObserver {
constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
readonly attribute (Element or Document)? root;
readonly attribute DOMString rootMargin;
readonly attribute DOMString scrollMargin;
readonly attribute FrozenArray&lt;double&gt; thresholds;
undefined observe(Element target);
undefined unobserve(Element target);
Expand Down Expand Up @@ -282,6 +284,20 @@ interface IntersectionObserver {
passed to the {{IntersectionObserver}} constructor. If no
{{IntersectionObserverInit/rootMargin}} was passed to the {{IntersectionObserver}}
constructor, the value of this attribute is "0px 0px 0px 0px".
: <dfn>scrollMargin</dfn>
::
Offsets are applied to <a>scrollports</a> on the path from <a>intersection root</a> to <a for="IntersectionObserver">target</a>,
tcaptan-cr marked this conversation as resolved.
Show resolved Hide resolved
effectively growing or shrinking the boxes that are used to calculate intersections.
tcaptan-cr marked this conversation as resolved.
Show resolved Hide resolved
<b>These offsets are only applied when handling <a>same-origin-domain targets</a>;
for <a>cross-origin-domain targets</a> they are ignored.</b>

On getting, return the result of serializing the elements of {{[[scrollMargin]]}}
space-separated, where pixel lengths serialize as the numeric value followed by "px",
and percentages serialize as the numeric value followed by "%". Note that
this is not guaranteed to be identical to the |options|.{{IntersectionObserverInit/scrollMargin}}
passed to the {{IntersectionObserver}} constructor. If no
{{IntersectionObserverInit/scrollMargin}} was passed to the {{IntersectionObserver}}
constructor, the value of this attribute is "0px 0px 0px 0px".
: <dfn>thresholds</dfn>
::
A list of thresholds, sorted in increasing numeric order,
Expand Down Expand Up @@ -321,15 +337,28 @@ with positive lengths indicating an outward offset.
Percentages are resolved relative to the width of the undilated rectangle.

Note: {{IntersectionObserver/rootMargin}} only applies to the <a>intersection root</a> itself.
If a <a>target</a> {{Element}} is clipped by an ancestor other than the
If a <a for="IntersectionObserver">target</a> {{Element}} is clipped by an ancestor other than the
<a>intersection root</a>, that clipping is unaffected by
{{IntersectionObserver/rootMargin}}.

Note: <a>Root intersection rectangle</a> is not affected by
When calculating a <a>scrollport</a> intersection rectangle for
a <a>same-origin-domain target</a>, the rectangle is expanded
according to the offsets in the {{IntersectionObserver}}’s {{[[scrollMargin]]}} slot
in a manner similar to CSS's <a href="https://www.w3.org/TR/css-box-3/#margins">margin</a> property,
with the four values indicating the amount the top, right, bottom, and left edges, respectively, are offset by,
with positive lengths indicating an outward offset.
Percentages are resolved relative to the width of the undilated rectangle.

Note: {{IntersectionObserver/scrollMargin}} affects the clipping of <a for="IntersectionObserver">target</a>
by all scrollable ancestors up to and including the <a>intersection root</a>.
tcaptan-cr marked this conversation as resolved.
Show resolved Hide resolved
Both the {{IntersectionObserver/scrollMargin}} and the {{IntersectionObserver/rootMargin}}
are applied to a scrollable <a>intersection root's</a> rectangle.

Note: <a>Root intersection rectangle</a> and <a>scrollport</a> intersection rectangles are not affected by
<a>pinch zoom</a> and will report the unadjusted <a>viewport</a>, consistent with the
intent of pinch zooming (to act like a magnifying glass and NOT change layout.)

To <dfn>parse a root margin</dfn>
To <dfn>parse a margin</dfn> (root or scroll)
from an input string |marginString|,
returning either a list of 4 pixel lengths or percentages,
or failure:
Expand Down Expand Up @@ -438,6 +467,7 @@ The IntersectionObserverInit dictionary</h3>
dictionary IntersectionObserverInit {
(Element or Document)? root = null;
DOMString rootMargin = "0px";
DOMString scrollMargin = "0px";
(double or sequence&lt;double>) threshold = 0;
};
</pre>
Expand All @@ -459,6 +489,13 @@ dictionary IntersectionObserverInit {
"-10px 5px 8px" // top = -10px, right & left = 5px, bottom = 8px
"-10px -5px 5px 8px" // top = -10px, right = -5px, bottom = 5px, left = 8px
</pre>
: <dfn>scrollMargin</dfn>
::
Similar to {{IntersectionObserverInit/rootMargin}},
this is a string of 1-4 components,
each either an <a>absolute length</a> or a percentage.

See {{IntersectionObserverInit/rootMargin}} above for the example.
: <dfn>threshold</dfn>
::
List of threshold(s) at which to trigger callback.
Expand All @@ -468,7 +505,7 @@ dictionary IntersectionObserverInit {

Threshold values must be in the range of [0, 1.0] and represent a
percentage of the area of the rectangle produced
by <a>getting the bounding box</a> for <a>target</a>.
by <a>getting the bounding box</a> for <a for="IntersectionObserver">target</a>.

Note: 0.0 is effectively "any non-zero number of pixels".
</div>
Expand Down Expand Up @@ -510,8 +547,9 @@ IntersectionObserver</h4>
which are initialized to empty lists and an internal
<dfn attribute for=IntersectionObserver>\[[callback]]</dfn> slot
which is initialized by {{IntersectionObserver(callback, options)}}</a>.
They also have an internal <dfn attribute for=IntersectionObserver>\[[rootMargin]]</dfn> slot
which is a list of four pixel lengths or percentages.
They also have internal <dfn attribute for=IntersectionObserver>\[[rootMargin]]</dfn>
and <dfn attribute for=IntersectionObserver>\[[scrollMargin]]</dfn> slots
which are lists of four pixel lengths or percentages.

<h3 id='algorithms'>
Algorithms</h2>
Expand All @@ -523,20 +561,25 @@ and an {{IntersectionObserverInit}} dictionary |options|, run these steps:

1. Let |this| be a new {{IntersectionObserver}} object
2. Set |this|'s internal {{[[callback]]}} slot to |callback|.
3. Attempt to <a>parse a root margin</a>
3. Attempt to <a>parse a margin</a>
from |options|.{{IntersectionObserverInit/rootMargin}}.
If a list is returned,
set |this|'s internal {{[[rootMargin]]}} slot to that.
Otherwise, <a>throw</a> a {{SyntaxError}} exception.
4. Let |thresholds| be a list equal to
4. Attempt to <a>parse a margin</a>
from |options|.{{IntersectionObserverInit/scrollMargin}}.
If a list is returned,
set |this|'s internal {{[[scrollMargin]]}} slot to that.
Otherwise, <a>throw</a> a {{SyntaxError}} exception.
5. Let |thresholds| be a list equal to
|options|.{{IntersectionObserverInit/threshold}}.
5. If any value in |thresholds| is less than 0.0 or greater than
6. If any value in |thresholds| is less than 0.0 or greater than
1.0, <a>throw</a> a {{RangeError}} exception.
6. Sort |thresholds| in ascending order.
7. If |thresholds| is empty, append <code>0</code> to |thresholds|.
8. The {{IntersectionObserver/thresholds}} attribute getter will return
7. Sort |thresholds| in ascending order.
8. If |thresholds| is empty, append <code>0</code> to |thresholds|.
9. The {{IntersectionObserver/thresholds}} attribute getter will return
this sorted |thresholds| list.
9. Return |this|.
10. Return |this|.

<h4 id='observe-target-element'>Observe a target Element</h4>

Expand Down Expand Up @@ -619,7 +662,7 @@ run these steps:
<h4 id='calculate-intersection-rect-algo'>
Compute the Intersection of a Target Element and the Root</h4>

To <dfn>compute the intersection</dfn> between a <a>target</a> |target| and an <a>intersection root</a> |root|,
To <dfn>compute the intersection</dfn> between a <a for="IntersectionObserver">target</a> |target| and an <a>intersection root</a> |root|,
run these steps:

1. Let |intersectionRect| be the result of <a>getting the bounding box</a> for |target|.
Expand All @@ -630,9 +673,11 @@ run these steps:
of the {{document}}, and update |container| to be
the <a>browsing context container</a> of |container|.
2. Map |intersectionRect| to the coordinate space of |container|.
3. If |container| has a <a>content clip</a> or a css <a>clip-path</a> property,
3. If |container| is scrollable, apply the {{IntersectionObserver}}’s
{{[[scrollMargin]]}} to the |container|'s clip rect.
4. If |container| has a <a>content clip</a> or a css <a>clip-path</a> property,
update |intersectionRect| by applying |container|'s clip.
4. If |container| is the root element of a <a>browsing context</a>,
5. If |container| is the root element of a <a>browsing context</a>,
update |container| to be the <a>browsing context</a>'s {{document}};
otherwise, update |container| to be the <a>containing block</a>
of |container|.
Expand Down Expand Up @@ -753,14 +798,14 @@ The main privacy concerns associated with this API relate to the information
it may provide to code running in the context of a cross-origin iframe
(i.e., the <a>cross-origin-domain target</a> case). In particular:

* There is no universal consensus on the privacy implications of
* There is no universal consensus on the privacy implications of
revealing whether an iframe is within the global viewport.

* There is a risk that the API may be used to probe for information
* There is a risk that the API may be used to probe for information
about the geometry of the global viewport itself,
which may be used to deduce the user's hardware configuration.
The motivation for disabling the effects of {{IntersectionObserver/rootMargin}}
and suppressing {{IntersectionObserverEntry/rootBounds}}
and {{IntersectionObserver/scrollMargin}}, and suppressing {{IntersectionObserverEntry/rootBounds}}
for <a>cross-origin-domain targets</a> is to prevent such probing.

It should be noted that prior to {{IntersectionObserver}}, web developers
Expand Down