Skip to content
This repository has been archived by the owner on Jun 3, 2020. It is now read-only.

Polyfill #3

Closed
jeremenichelli opened this issue May 23, 2016 · 31 comments
Closed

Polyfill #3

jeremenichelli opened this issue May 23, 2016 · 31 comments

Comments

@jeremenichelli
Copy link

In case this goes on I would like to try wirting a polyfill for it, I'm writing this as an issue because I dodn't know how to ask for that chance. Let me know if it's a better way.

@atotic
Copy link
Collaborator

atotic commented May 23, 2016

You do not need anyone's permission, it's an open standard :)

The spec is not final yet. I am not sure if we'll end up observing clientWidth or something else, there will be a bug filed about this today.

Polyfill is an interesting topic, there is a tension between API correctness, and performance.

The obvious polyfill is to do size polling in requestAnimationFrame. The downside is wasted CPU/power.

Others have tried to create more performant resize detectors: https://github.com/wnr/element-resize-detector/ is very good, but they all have their quirks.

@jeremenichelli
Copy link
Author

Thanks for the response @atotic. As soon as I have time I'll fork and branch this to start playing a little with the standard, even if it's not finished it could arise needs or caveats around the API. Also, as I was reading the spec my first idea was to poll using rAF plus some deboucing since it would stop doing it when the tab is not visible and its polyfill is short to include on top of a project.

@atotic
Copy link
Collaborator

atotic commented Jun 23, 2016

I will add any polyfill attempts here:

https://github.com/pelotoncycle/resize-observer

@atotic
Copy link
Collaborator

atotic commented Aug 3, 2016

https://github.com/que-etc/resize-observer-polyfill

Implements event based tracking of changes in elements dimensions. Uses MutationsObserver and falls back to an infinite dirty checking cycle if the first one is not supported. Handles long running CSS transitions/animations, attributes and nodes mutations along with changes made by :hover pseudo-class (optional).

Written in ES6 and compliant with the spec. Doesn't contain any publicly available methods or properties except for those described in the spec. Size is 4.4kb when minified and gzipped.

@ShimShamSam
Copy link

ShimShamSam commented Oct 13, 2016

https://github.com/ShimShamSam/ResizeListener
How it works:
Hidden elements are appended to the elements you want to monitor. These hidden elements exploit their scrollbar behavior when they change in size and react to the corresponding scroll events. The events are then aggregated and throttled using requestAnimationFrame (or setTimeout in older browsers) to make a fast and efficient way to listen for changes in element size.

@NekR
Copy link

NekR commented Jan 4, 2017

Here is another attempt to watch element resizes. Very simple and straightforward, also small (~350b even not minified). Not a polyfill for this exact spec though.

@atotic
Copy link
Collaborator

atotic commented Jan 4, 2017

@NekR you did not include a link?

@NekR
Copy link

NekR commented Jan 4, 2017

@atotic oh, sorry, silly me. Here it is: https://github.com/developit/simple-element-resize-detector

@NekR
Copy link

NekR commented Jan 4, 2017

I'm using approach from simple-element-resize-detector now in a project to customize browser scrollbars. Works great even on iOS where iframes have its limitations.

@trusktr
Copy link

trusktr commented Mar 18, 2017

@atotic

The spec is not final yet. I am not sure if we'll end up observing clientWidth or something else, there will be a bug filed about this today.

Don't observe clientWidth or clientHeight. We need floating point values that represent what is actually rendered on screen. clientWidth and clientHeightare, for all intents and purposes, values aliased to CSS pixels (integers), which means if we observe those then some parent element (for example) can be visually changing size on the screen while no ResizeObserver callbacks fire at all because the change in size is a sub-integer amount (for example going from 10.0 to 10.49).

Content that is on the plane of the display (not zoomed, no Z transform with perspective, no X or Y rotation with perspective) would be fine with clientWidth observation because in that case the rendering aliases directly to CSS pixels. Content that is zoomed, and not most of all 3D content will not be aliased to the screen's CSS pixels. For example, content that is translateZ(200px) (between the display plane and the user's eyes) will visibly grow with floating-point values of width and height (for example, getComputedStyle can show a width of 100.234px).

We need to be able to react to those changes and (for example) render WebGL content appropriately without visual disparity bugs that would arise from observing values aliased to CSS pixels.

getComputedStyle returns accurate float values, but it is also heavy. I don't think there's any alternative to it, which is why the native ResizeObserver will be really nice.

@rjgotten
Copy link

rjgotten commented Mar 31, 2017

@trusktr
A primary use-case (if not the primary use-case) for ResizeObserver would be to adapt an element's contents to its available layout space; serving as an enabler for container / element queries. Making ResizeObserver monitor screen space size changes is not really compatible with that, I believe.

@trusktr
Copy link

trusktr commented Apr 7, 2017

@rjgotten

A primary use-case (if not the primary use-case) for ResizeObserver would be to adapt an element's contents to its available layout space [in its parent];

Yes, true.

But you've misunderstood. You said:

Making ResizeObserver monitor screen space size changes is not really compatible with that, I believe.

  1. However clientWidth and clientHeight properties don't exist just on window, they exist on every element in the DOM, so I am talking about measuring the size of any Element in order to resize a child element (for example).
  2. But I'm also against using clientWidth and clientHeight. I've updated my previous comment to make it more clear by mentioning getComputedStyle, which also exists on every element.

@trusktr
Copy link

trusktr commented Apr 7, 2017

@rjgotten So if you could change that to 👍, that would be nice. :D

@trusktr
Copy link

trusktr commented Apr 7, 2017

@atotic I think getComputedStyle is the only option for now (see my explanation above). Have I missed anything besides el.clientWidth, el.clientHeight, parseFloat(el.getComputedStyle().width), and parseFloat(el.getComputedStyle().height)?

@atotic
Copy link
Collaborator

atotic commented Apr 7, 2017

@trusktr This is a long thread, unsure what is being asked.

  • ResizeObserver observes content size, It will report all changes, even if they are fractional.
  • ResizeObserver does not observe screen size of an element, so translate transformations will not be reported.

@trusktr
Copy link

trusktr commented Apr 7, 2017

@atotic Up above you said

The spec is not final yet. I am not sure if we'll end up observing clientWidth or something else, there will be a bug filed about this today.

I was simply just saying don't use clientWidth/clientHeight, because those are integers, not floats. Currently the only thing I know of that return floats (as far as polyfills go) is getComputedStyle.

ResizeObserver observes content size, It will report all changes, even if they are fractional.

So I guess you maybe thought of that already, but wasn't clear in your previous post about clientWidth which isn't fractional.

ResizeObserver does not observe screen size of an element, so translate transformations will not be reported.

What I was saying about transforms is that we need fractional sizing values of element sizes so when transforms are applied we don't see disparity between a transformed element and it's child where the child size is using ResizeObserver to size based on the transformed parent. What would happen is that the size of the parent can appear to change in each animation frame, while the child size (if using clientWidth) would appear to change size only when the parent's rounded size value changed to the nearest integer which may not be every frame.

So if you're not planning to use clientWidth but rather a floating-point value, then everything is all good!

@jeremenichelli
Copy link
Author

@trusktr getBoundingClientRect also returns floats if I'm not wrong.

@atotic
Copy link
Collaborator

atotic commented Apr 7, 2017

Yeah, we are watching floats. Also, reporting floats.

@NekR
Copy link

NekR commented Oct 24, 2017

I made ResizeObserver polyfill with IntersectionObserver. https://github.com/NekR/ResizeObserver-Intersect

To work, observer target has to be of position: relative. Other differences:

  • ResizeObserver reports float numbers and polyfill don't.
  • IntersectionObserver may miss frames if something other keeps main thread busy. The problem is that may miss latest frames and report slightly different size from reality, even with big threshold (1000).

@NekR
Copy link

NekR commented Oct 24, 2017

Demo: https://jsfiddle.net/c4hmwzaz/7
(requires browser with ResizeObserver, e.g. Chrome with Experimental Web Features flag turned on)

@hoIIer
Copy link

hoIIer commented Nov 13, 2017

what's the latest status on ResizeObserver? when can we expect to be able to use it for development?

@atotic
Copy link
Collaborator

atotic commented Nov 13, 2017

Just turned it on by default in M64.

https://bugs.chromium.org/p/chromium/issues/detail?id=612962

@hoIIer
Copy link

hoIIer commented Nov 13, 2017

@atotic nice! so this means it will be in chrome soon? (not behind feature flag) any idea on ff/safari/ie? the use case I'm trying to tackle which it seems like it may work well for is keeping a chat thread scrolled to the bottom **reliably. The current best solution seems to be el.scrollTop = el.scrollHeight but that isn't always reliable especially when rendering initial message sets that sometimes contain longer messages and result in timing issues etc

@atotic
Copy link
Collaborator

atotic commented Nov 13, 2017

It should be on Dev channel on Nov 14th.
I can't comment on ff/safari/ie. FF had an intern effort last year, but it did not ship. All other vendors were supportive, and it is not very hard to implement, so my hope is "within a year", but I have no data to support this.
Have you seen the latest chat example: https://rawgit.com/WICG/ResizeObserver/master/examples/chat.html
Curious how well it would work with polyfills...

@ZeeCoder
Copy link

Chrome'll ship ResizeObserver this month!
Here's a test link to see it's effects:
https://codesandbox.io/s/l3rmm1rz2l

Just compare Chrome 63 and 64, where the latter has the native implementation.

@bertyhell
Copy link

I had also made this resize observer which uses an empty iframe and then listens for the resize event of the inner iframe page. Not sure what the performance is. This isn't wrapped as a polyfill yet. Also doesn't work correctly when inside scrollable divs.

https://github.com/bertyhell/element-height-observer

@rjgotten
Copy link

rjgotten commented Sep 10, 2018

Not sure what the performance is.

It'll use quite a bit more memory than using an <object> element, because an iframe needs an entire new browsing context with more weight attached than an object.

Whether using <iframe> or <object> element, the method will also be signficantly less performant than the double-scroll method of detecting resizes, which can perform up to 20x better when written well -- i.e. when avoiding DOM thrashing.

@TremayneChrist
Copy link

TremayneChrist commented Jan 19, 2019

I created a polyfill which aims to be as close as possible to the draft specification.
https://github.com/juggle/resize-observer

Tested Browser Desktop Mobile
Chrome
Safari
Firefox
Opera
Opera Mini N/A
Samsung Internet N/A
IE11 N/A
Edge

Feedback is welcomed on this!

@atotic
Copy link
Collaborator

atotic commented Jan 20, 2019

@TremayneChrist Thanks for another one, glad that I do not have to be doing this work.

Your approach is to watch for events that might change size (resize, animations, mutations). Will this work for something like el.style.width = "100px"?

ResizeObserver spec has moved to CSS Working Group. We've been working on extending the API to allow watching different boxes. The new spec is at https://drafts.csswg.org/resize-observer-1/. We do not have a polyfill for new spec yet.

If you have any feedback on the spec, please leave it on https://github.com/w3c/csswg-drafts.

@TremayneChrist
Copy link

Hey @atotic,

el.style.width = 100px is supported. The library listens to events which could cause changes via user interaction or animations/transitions. It also uses mutation observers for attribute changes and addition/deletion of nodes.

I looked at the new spec, however, due to the issues/questions around some parts of it, I decided to continue with the older one. On second thoughts, I could add the new box sizes and keep the contentRect, without the deprecation warning for now, as the only other differences seem to be internal.

Will have a think and possibly try and get this into the v1 release.

@TremayneChrist
Copy link

@atotic I've updated the polyfill so that it can now watch different box sizes.

Would be good to get some feedback from everyone, on using this new approach to observing elements. I'm personally not sure about the new property names that are being returned in the entry, but, maybe they will grow on me :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests