joicomponents
Version:
Design patterns to build native web components
107 lines (91 loc) • 5.57 kB
Markdown
# ResizeMixin
The purpose of `ResizeMixin` is to provide a reactive method called `resizeCallback(contentRect)`
and the method `getContentRect()`.
The ResizeMixin observes the contentRectangle of the custom element, and then triggers
`resizeCallback` method every time the size of the contentRectangle of the webcomponent changes (once per frame).
`resizeCallback` has two primary use-cases:
1. web-component mediaquery: You need to change the style or the innerDOM of a custom element based on
its available screen size.
2. You want to change some attributes of dependent elements (such as size or position)
based on a combination of size and/or content (cf. [SlotchangeMixin](../../../trash/book/chapter3_element_lifecycle/chapter3_lifecycle/chapter3_old/Mixin1_SlotchangeMixin.md)).
### Example of use:
```javascript
import {ResizeMixin} from "./ResizeMixin.js";
class MyWebComponent extends ResizeMixin(HTMLElement) { //[1a]
resizeCallback({width, height}) { //[2]
console.log(`my inner dimensions are: width: ${width}px, height: ${height}px`);
}
}
customElements.define("my-web-component", MyWebComponent); //[1b]
const el = new MyWebComponent();
el.style.width = "100px"; //[3]
document.querySelector("body").appendChild(el); //[4]
setTimeout( () => el.style.width = "200px", 100); //[5]
```
1. Adding the functional mixin as on a custom element:
`ResizeMixin(HTMLElement)`
`customElements.define("my-web-component", MyWebComponent);`
2. `resizeCallback({width, height})` is called every time the contentRectangle's width or height
changes, while the element is connected to the DOM.
3. `resizeCallback()` *is not* triggered here since `el` is not connected to DOM.
4. `resizeCallback()` *gets* triggered here as `el` gets connected to DOM.
5. Because `resizeCallback()` is only observed during "requestAnimationFrame-time" or during "ResizeObserver-time"
and once per frame per element, to trigger a second callback,
we must delay the trigger until a later point (here using `setTimeout(..., 100)`).
## getContentRect()
`getContentRect(cachedOnly)` is a function that returns the "inner size" of the element in which
shadowDom and children can be rendered. The "inner size" of the element is defined as
```javascript
{
width: window.getComputedStyle(this).width,
height: window.getComputedStyle(this).height
}
```
If `true` is passed as parameter, then only the last cached value of the contentRect will be supplied and no
LayooutThrashing call to `getComputedStyle()` will be made.
### Pattern based .resizeCallback for fullscreen resizes?
If you are tracking the full screen size, use this pattern instead of the sizeChangedMixin:
```javascript
class MyWebComponent extends HTMLElement {
constructor(){
super();
this._resizeListener = e => this.resizeCallback({width: window.innerWidth, height: window.innerHeight});
}
connectedCallback(){
window.addEventListener("resize", this._resizeListener);
this.resizeCallback({width: window.innerWidth, height: window.innerHeight});
}
disconnectedCallback(){
window.removeEventListener("resize", this._resizeListener);
}
resizeCallback({width, height}) {
//the components style can likely be controlled by css mediaqueries directly here.
//But if you want to change the shadowDOM, you should do so here.
//make sure you cache your last width and height, so that you don't redraw the shadowDom everytime you don't want.
}
}
customElements.define("my-web-component", MyWebComponent);
```
This is a direct parallel to css mediaquery.
The benefit of using this js based pattern for web components,
is that you can alter both the style and the shadowDom of your webcomponents here.
## Implementation details
1. Chrome's `ResizeObserver` runs three-order after layout at a special "ResizeObserver-time".
In other browsers, this is done in the "requestAnimationFrame-time".
2. `ResizeMixin` handles **no css transforms**, neither at ResizeObserver-time nor requestAnimationFrame-time.
This is ok because the size of the children are transformed same as the parent.
If you really need to handle css transforms as well, then check, cache and parse the style.transform
tag at requestAnimationFrame-time.
3. Chrome's `ResizeObserver` does not observe `{display: inline}` elements.
4. In Chrome, the `contentRect` parameter created by `ResizeObserver` can differ slightly from
the definition of `getContentRect()` that uses `getComputedStyle()`.
#### References
* https://wicg.github.io/ResizeObserver/
* https://github.com/WICG/ResizeObserver/blob/master/explainer.md
* https://gist.github.com/paulirish/5d52fb081b3570c81e3a
* https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing
* https://googlechrome.github.io/samples/resizeobserver/
* https://github.com/WICG/ResizeObserver/issues/3
* https://github.com/w3ctag/design-principles/issues/78
* https://github.com/wnr/element-resize-detector
* https://github.com/ebidel/demos/blob/master/dom_resize_events.html#L152-L170