️ Use RO singleton instead of iframe if supported

pull/8019/head
TheCymaera 4 years ago
parent 91f8764145
commit 380583804c

@ -0,0 +1,46 @@
const MapImplementation = "WeakMap" in window ? WeakMap : Map;
/**
* Resize observer singleton
* https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ
*/
export class ResizeObserverSingleton {
constructor(readonly options?: ResizeObserverOptions) {}
addListener(element: Element, callback: (element: Element)=>any) {
if (!this._callbacks.has(element)) {
this._callbacks.set(element, new Set([callback]));
this._getObserver().observe(element, this.options);
} else {
this._callbacks.get(element)!.add(callback);
callback(element);
}
}
removeListener(element: Element, callback: (element: Element)=>any) {
const list = this._callbacks.get(element);
if (!list) return;
if (list.size > 1) {
list.delete(callback);
} else {
this._callbacks.delete(element);
this._getObserver().unobserve(element);
}
}
// TODO: consider using Array instead of Set?
// If we can guarantee one listener per element, we can simplify even more.
private readonly _callbacks: WeakMap<Element, Set<(element: Element)=>any>> = new MapImplementation();
private _observer: ResizeObserver|undefined = undefined;
private _getObserver() {
return this._observer ?? (this._observer = new ResizeObserver((entries)=>{
for (const entry of entries) {
const element = entry.target;
const callbacks = this._callbacks.get(element);
if (callbacks) {
for (const callback of callbacks) callback(element);
}
}
}));
}
}

@ -1,3 +1,4 @@
import { ResizeObserverSingleton } from './ResizeObserverSingleton.js';
import { has_prop } from './utils'; import { has_prop } from './utils';
// Track which nodes are claimed during hydration. Unclaimed nodes can then be removed from the DOM // Track which nodes are claimed during hydration. Unclaimed nodes can then be removed from the DOM
@ -596,7 +597,7 @@ export function is_crossorigin() {
return crossorigin; return crossorigin;
} }
export function add_resize_listener(node: HTMLElement, fn: () => void) { function add_iframe_resize_listener(node: HTMLElement, fn: () => void) {
const computed_style = getComputedStyle(node); const computed_style = getComputedStyle(node);
if (computed_style.position === 'static') { if (computed_style.position === 'static') {
@ -640,6 +641,31 @@ export function add_resize_listener(node: HTMLElement, fn: () => void) {
}; };
} }
export const resize_observer_content_box = new ResizeObserverSingleton({ box: "content-box" });
// export const resize_observer_border_box = new SoleResizeObserver({ box: "border-box" });
// export const resize_observer_device_pixel_content_box = new SoleResizeObserver({ box: "device-pixel-content-box" });
export function add_content_box_observer(node: HTMLElement, fn: () => void) {
resize_observer_content_box.addListener(node, fn);
return ()=> resize_observer_content_box.removeListener(node, fn);
}
// export function add_border_box_observer(node: HTMLElement, fn: () => void) {
// resize_observer_border_box.addListener(node, fn);
// return ()=> resize_observer_border_box.removeListener(node, fn);
// }
//
// export function add_device_pixel_content_box_observer(node: HTMLElement, fn: () => void) {
// resize_observer_device_pixel_content_box.addListener(node, fn);
// return ()=> resize_observer_device_pixel_content_box.removeListener(node, fn);
// }
export const add_resize_listener = "ResizeObserver" in window ?
add_content_box_observer :
add_iframe_resize_listener;
export function toggle_class(element, name, toggle) { export function toggle_class(element, name, toggle) {
element.classList[toggle ? 'add' : 'remove'](name); element.classList[toggle ? 'add' : 'remove'](name);
} }

Loading…
Cancel
Save