w3c / intersectionobserver Goto Github PK
View Code? Open in Web Editor NEWIntersection Observer
Home Page: https://www.w3.org/TR/intersection-observer/
License: Other
Intersection Observer
Home Page: https://www.w3.org/TR/intersection-observer/
License: Other
There's no need for an O(n) observation here. You just need a placeholder item above and a placeholder item below. So, you only need to observe two elements.
Also, this example needs to more clearly show that the items are recycled to make it clear that there can never be that many items in the scroller.
Both of these have been confusing to just about everyone who has read the explainer.
@slightlyoff @esprehn, one of you feel like you have time to fix this example?
I'm pretty sure this only leaks information you can already get to via polling the window on the iframe, but we should double check that to be 100% sure.
IntersectionObserverEntry objects are not exotic and there's no reason to keep their creation magic that I can see. They should have a constructor. I'd suggest one that takes a dictionary
dictionary IntersectionObserverEntryInit {
required DOMHighResTimeStamp time;
required DOMRectInit rootBounds;
required DOMRectInit boundingClientRect;
required DOMRectInit intersectionRect;
required Element target;
}
You could try to add smart defaults instead of making everything required but that doesn't seem worth the trouble.
I found https://rawgit.com/slightlyoff/IntersectionObserver/master/index.html on blink-dev but I can't find it from the readme.
Bonus points: make it the repo's website. (See e.g. the headline at https://github.com/whatwg/html at the top of the page.) I think only @slightlyoff can do that though.
All the processing model algorithms make reference to Document. But this is not defined. I assume it's meant to be a Document of some sort. But which one? From which window? This can be especially bad when e.g. code in one frame uses the IntersectionObserver constructor from another frame to observe an Element from a third frame.
Edit: Overall, the Explainer is out of date. Needs to be updated to the current spec. Below describes just one difference, not comprehensive.
The interfaces don't have the same methods. The spec has DOMString threshold = "1px"
, the explainer has boolean thresholdCallbacks = true
.
https://github.com/slightlyoff/IntersectionObserver/blob/master/index.bs#L235
dictionary IntersectionObserverInit {
..[snip]..
DOMString threshold = "1px";
};
https://github.com/slightlyoff/IntersectionObserver/blame/master/explainer.md#L54
(Sorry for linking to the blame, I couldn't figure out how to link directly to a line in the markdown)
dictionary IntersectionObserverInit {
..[snip]..
// Whether to give callbacks only when an element starts/stops intersecting
// the root bounds or everytime it changes how much it intersects.
// Callback only fires if the element isn’t intersecting an edge of the
// viewport in the case that the element jumps from being entirely outside
// the viewport to entirely inside it.
// Defaults to true, a less power-hungry option.
boolean thresholdCallbacks = true;
};
readonly DOMRect rootBounds
means that entry.rootBounds = {}
fails; readonly DOMRectReadOnly rootBounds
means that entry.rootBounds.x = -1
fails. Unless there are sensible semantics for allowing changes to the rects, it seems better to make them DOMRectReadOnlys.
See also Issue #1
We are hoping for a much more robust definition and specification of the browser's viewport model (e.g., with regard to zooming) before exposing this data to web developers--it seems a bit premature to reveal it through this feature.
As far as implementation, we do have a PVR (paint-ahead bounding rect around the visible viewport) that we would not want to make observable or standardize because we view it as an implementation detail--we don't think those kinds of internal details should be the basis for signaling visibility because they would be difficult to get interoperable behavior.
E.g. in https://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#element-has 3.1.3 step 2.
Unfortunately, DomHighResTimeStamp
doesn't have a well-defined zero time (thank you, web perf working group), so whenever you use it you have to say what your zero time is... You probably want the navigationStart of something, but please coordinate with the webperf people for the right way to phrase this, especially in cases when a single Window
has multiple Document
s or vice versa.
It is currently
Constructor(IntersectionObserverCallback callback, IntersectionObserverInit options)
It must instead be
Constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options)
If the type of an argument is a dictionary type or a union type that has a dictionary type as one of its flattened member types, and that dictionary type and its ancestors have no required members, and the argument is either the final argument or is followed only by optional arguments, then the argument MUST be specified as optional
We should throw (or silently fail) if you call observe on an element that is not a descendant of the viewport element. Similarly, we need to handle when an observed element moves to a different subtree.
Should it be a DOMQuad? There were some concerns in Blink about shipping DOMMatrix, would the same apply to DOMQuad?
Does the proposal anticipate PositionObserver usage on inline elements? We assume so, and in that case, are the quads supposed to mirror getClientRects()?
While we think the modeling of this feature on the MutationObserver pattern is good and familiar, we do wonder if a far-simpler design could be used to address the ad case. We see two common questions:
The key aspects to preserve are:
In our quick brainstorm, the simplest solution was an event (e.g., "isvisible") that is fired to the element in question and carries some data about it's occlusion state (fully, partially, rect list...?). The occlusion state could be accurate enough for author code to determine percentage visibility, and/or what other elements are above it in stacking context (e.g., something like elementsFromPoint
, but for a rectangle... elementsFromRect
?).
We also think a Promise-based isVisible() or some-such will be a strongly-requested feature.
We think a simple way of extending the target/viewport dimensions is also useful.
Currently it just says "pre-paint window". I don't know what that means, what units it's in, what it's relative to, etc.
Specifically, the definitelyVisibleRect for doing occlusion detection.
Maybe include time to viewport modifier?
There are a number of slight differences in design from MutationObserver:
These changes are probably well-motivated, but since I'm not too familiar with the design space, I find the mismatch surprising. If they are intentional, it would be nice to add a non-normative note explaining them. It might be the case that MutationObserver has flaws and this is a slightly better iteration.
I think we should remove the concept until we find a use-case for it. With it being async, I don't see any benefit to it.
We just need to make sure that if the element jumps from fully outside the viewport to fully inside or vice versa that you still get a callback.
The spammy thresholdCallbacks: false is just a performance foot-gun without a use-case.
@esprehn @slightlyoff sound ok?
We're both critical of this scenario and supportive of it. We've definitely seen lazy image loaders be a scenario that authors could consider solving with PositionObserver (versus their current approach of spamming scroll events). In our rough brainstorm, we came up with a much more direct solution to solving this problem that doesn't need all the PositionObserver machinery as only a simple notice is needed just before the node would come into view. See Alternative Ideas in #4 applied to <img>
elements.
E.g. talking about "Document.pendingCallbacks". I appreciate the efforts at precision to talk precisely about what internal slots each object has. In new specs that define their own objects I suggest notation like "Document@[[pendingCallbacks]]" and explicit discussion of internal slots.
For this case though, since you are extending objects defined in other specs which don't use those concepts, the use of dot notation is confusing. It is easy to think you are talking about normal JavaScript property access.
I'd suggest just going with ye olde "Document's pendingCallbacks".
Since the internal properties of an object are shared across all specs, you need to be careful not to take up names that are too generic. For example, Document's "pendingCallbacks" or Element's "registeredObservers" are squatting on pretty valuable territory. Documents will probably have a variety of pending callbacks, and nodes already have a list of registered observers---I hope we aren't supposed to distinguish between "Element's registeredObservers" and "Element's registered observers."
Specifically, need to define what all the relative units are relative to: which viewports, which font-sizes, etc.
The list view scenario is not fully covered. In Microsoft's first version of the WinJS virtualized list view, we had an implementation similar to the one that this proposal tries to help facilitate. However, our experience with the v1 showed that frequent element cycling/thrashing led to terrible perf (in all UAs). Now in our v2+ we preload a larger subset of elements to avoid too frequence cycling, and this works better for avoiding the flashes of white and DOM thrashing as well.
There's a lot of code in our virtual list view for maintaining the virtual content length (for accurate scrollbar calculation). This is tightly coupled with addition/removal of virtual contents and is not really addressed by the proposal.
In the virtual list view case, there's usually not one specific element target which is the basis for determining when and how to hydrate/de-hydrate elements. To that end, the PositionObserver does not provide a direct solution here (though it might be useful indirectly by way of extending an element target's rectangle).
In general, we don't think the virtual list view scenario is/should be a target scenario for the feature. Its implementation is rather complicated and not likely fully addressed by PositionObserver.
We should just call it the root document's viewport or something explicit like that.
Posting a task seemed uncontroversial at the CSS F2F and is my preference.
An alternate proposed was to fire at the same time as scroll events, but noone seemed attached to that.
Not a big deal... might impact screen readers, but I don't know that for a fact. Just something I noticed while going through the source.
I can't see how this could possibly be used in workers.
Along with the change in #21, we should stop using the "viewport" term. Many folks find it confusing.
A few places in the spec refer to public API methods:
The DOMRect corresponding to the target’s getBoundingClientRect().
Let boundingClientRect be the value of target.getBoundingClientRect().
For each observer in node’s internal [[RegisteredIntersectionObservers]] slot, invoke unobserve() on observer, passing in node as target.
This is not quite the right thing to do, since it implies that e.g. if I do Element.prototype.getBoundingClientRect = () => { throw new Error("boo"); }
, you'll call that overriden function.
Instead, the correct thing to do is to refer to the backing concept or algorithm behind that method. For unobserve, you'd probably define something in the processing model like
"To remove an intersection observer o from an Element target, perform the following steps: ..."
Then unobserve() algorithm would do "Remove the intersection observer this from target," and instead of doing "invoke unobserve()," you would do a similar thing.
For getBoundingClientRect(), things are more annoying, since CSSOM View hasn't factored themselves appropriately. There you could do something like "Let boundingClientRect be a DOMRectReadonly obtained by the same algorithm as that of Element's getBoundingClientRect() performed on target."
There are use-cases for doing things when an element crosses a threshold inside a scroller.
At the CSS F2F, there was clear agreement that this is a common enough non-malicious case that the viewport rect should be the intersection of all the ancestor clipping due to overflow (hidden, auto, scroll).
To be clear, this should only apply if the viewport parameter is null, right?
The getComputedOpacity() query in the example needs to test all ancestors too. It should also test for ‘visibility’ on the element and ancestors.
Related to issue #10, what should happen if the viewport-defining element is an inline?
I see a couple options I'd be happy with:
This is not a use-case we need to support, so whatever is simplest.
Mozilla, Apple and Microsoft all felt that viewportModifier should only be a distance field. dbaron suggested that there should be an extra boolean that controls whether to fire the callback when the browser expects the element to intersect the modified viewport (e.g. due to scrolling) and all the vendors seemed OK with that.
Just use sequence<IntersectionObserverEntry>
instead.
Probably it should be the visual viewport, not the layout viewport?
We can't think of a good design that eliminates the need to perform layout for these scenarios; using our (off thread) display tree state may be out of date and not necessarily backed by a related DOM element. Perhaps there's something we're missing?
The design goals seem conflicting: don't necessarily run layout, yet be able to report thresholdCallbacks:false
and get quads with specific pixel-perfect dimensions. We feel like this should land firmly on the side of accurate reporting if we will go through the trouble of notifying at the specific times.
Rate of callback and time of callback should be more defined (or course). At least a rate limit would be nice (no more than 1 callback per 500 ms or some-such).
Otherwise this leaks the precise location of a cross-domain iframe within the top-level page.
Does it allow percentage values? calc()
? Units other than px
? If if allows em
and ex
units, which font size is used for them, and when? If it allows vh
and vw
units, which viewport is used for them and when?
Attempting to evaluate velocity and projected visibility of the target in terms of time seems exceptionally error-prone, hard to get interop, hard to test, etc. There is a clear desire to want an extended viewport dimension, and it seems much easer to go with something simple here (an extended border dimension, in pixels, for example would do fine--if you need more lead time, make the border bigger).
observe()
requires target to be a descendant of [[root]]
and the processing model in http://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#3-3-event-loop assumes that the target is a descendant of [[root]]
but nothing enforces that the ancestors of target don't change between the observe()
call and the event loop processing.
Occlusion seems pretty important (for ad impression accuracy). We think that occlusion should somehow be addressed in the solution.
Are the percent-in-view requirements really important? E.g., is this a deal-breaker for ad-impression tracking?
How do the quads relate to the trip-wire for what is considered in threshold? Is it the bounding box? Does this meet the accuracy requirements?
We assume various applied element transformations should be covered, as they are frequently used in advertising attention-grabbing strategies:
It looks like the UA is allowed to post a task or to post an idle request callback. Those have quite different observable behavior and it wouldn't surprise me if pages came to depend on one or the other in practice, leading them to work in some UAs but not others.
We should just pick a behavior here and define it, in my opinion.
This might be related to #35.
[[RegisteredIntersectionObservers]] is defined on Document and used in https://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#3-3-event-loop, but nobody ever adds to it or removes from it.
In contrast, another [[RegisteredIntersectionObservers]] is defined on Element and modified by IntersectionObserver's methods, but nobody ever reads from it.
Again it might be worth consulting what Mutation Observers do here.
We have lots of inconsistent time units in the platform, but using milliseconds for the viewportModifierTime
might be better.
@ojanvafai, WDYT?
In meeting with other browser vendors at the CSS F2F, they felt PositionObserver was too general of a name. Best alternate suggestion was IntersectionObserver.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.