mirror of
				https://github.com/jaandrle/deka-dom-el
				synced 2025-11-04 07:09:15 +01:00 
			
		
		
		
	💥 observedAttributes
				
					
				
			This commit is contained in:
		
							
								
								
									
										43
									
								
								dist/dde-with-observables.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								dist/dde-with-observables.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										29
									
								
								dist/dde.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								dist/dde.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										8
									
								
								dist/esm-with-observables.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								dist/esm-with-observables.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -121,7 +121,7 @@ document.head.append(
 | 
			
		||||
const interval= 5 * 1000;
 | 
			
		||||
setTimeout(clearInterval, 10*interval,
 | 
			
		||||
	setInterval(()=> count(count()+1), interval));
 | 
			
		||||
</code></div><script>Flems(document.getElementById("code-example-1-ehcq40v0h5k"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { O } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\nconst count= O(0);\\n\\nimport { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\ndocument.body.append(\\n\\tel(\\\"p\\\", O(()=> \\\"Currently: \\\"+count())),\\n\\tel(\\\"p\\\", { classList: { red: O(()=> count()%2) }, dataset: { count }, textContent: \\\"Attributes example\\\" })\\n);\\ndocument.head.append(\\n\\tel(\\\"style\\\", \\\".red { color: red; }\\\")\\n);\\n\\nconst interval= 5 * 1000;\\nsetTimeout(clearInterval, 10*interval,\\n\\tsetInterval(()=> count(count()+1), interval));\\n\"}],\"toolbar\":false}"));</script><p>To derived attribute based on value of observable variable just use the observable as a value of the attribute (<code>assign(element, { attribute: O('value') })</code>). <code>assign</code>/<code>el</code> provides ways to glue reactive attributes/classes more granularly into the DOM. Just use dedicated build-in attributes <code>dataset</code>, <code>ariaset</code> and <code>classList</code>.</p><p>For computation, you can use the derived observable (see above) like <code>assign(element, { textContent: O(()=> 'Hello '+WorldObservable()) })</code>.</p><p>To represent part of the template filled dynamically based on the observable value use <code>O.el(observable, DOMgenerator)</code>. This was already used in the todo example above or see:</p><!--<dde:mark type="component" name="example" host="this" ssr/>--><div id="code-example-2-8r8qappf8mo" class="example"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">import { O } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";
 | 
			
		||||
</code></div><script>Flems(document.getElementById("code-example-1-ehcq40v0h5k"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { O } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\nconst count= O(0);\\n\\nimport { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\ndocument.body.append(\\n\\tel(\\\"p\\\", O(()=> \\\"Currently: \\\"+count())),\\n\\tel(\\\"p\\\", { classList: { red: O(()=> count()%2) }, dataset: { count }, textContent: \\\"Attributes example\\\" })\\n);\\ndocument.head.append(\\n\\tel(\\\"style\\\", \\\".red { color: red; }\\\")\\n);\\n\\nconst interval= 5 * 1000;\\nsetTimeout(clearInterval, 10*interval,\\n\\tsetInterval(()=> count(count()+1), interval));\\n\"}],\"toolbar\":false}"));</script><p>To derived attribute based on value of observable variable just use the observable as a value of the attribute (<code>assign(element, { attribute: O('value') })</code>). <code>assign</code>/<code>el</code> provides ways to glue reactive attributes/classes more granularly into the DOM. Just use dedicated build-in attributes <code>dataset</code>, <code>ariaset</code> and <code>classList</code>.</p><p>For computation, you can use the “derived observable” (see above) like <code>assign(element, { textContent: O(()=> 'Hello '+WorldObservable()) })</code>. This is read-only observable its value is computed based on given function and updated when any observable used in the function changes.</p><p>To represent part of the template filled dynamically based on the observable value use <code>O.el(observable, DOMgenerator)</code>. This was already used in the todo example above or see:</p><!--<dde:mark type="component" name="example" host="this" ssr/>--><div id="code-example-2-8r8qappf8mo" class="example"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">import { O } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";
 | 
			
		||||
const count= O(0, {
 | 
			
		||||
	add(){ this.value= this.value + Math.round(Math.random()*10); }
 | 
			
		||||
});
 | 
			
		||||
@@ -147,4 +147,4 @@ setTimeout(clearInterval, 10*interval, setInterval(function(){
 | 
			
		||||
	O.action(count, "add");
 | 
			
		||||
	O.action(numbers, "push", count());
 | 
			
		||||
}, interval));
 | 
			
		||||
</code></div><script>Flems(document.getElementById("code-example-2-8r8qappf8mo"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { O } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\nconst count= O(0, {\\n\\tadd(){ this.value= this.value + Math.round(Math.random()*10); }\\n});\\nconst numbers= O([ count() ], {\\n\\tpush(next){ this.value.push(next); }\\n});\\n\\nimport { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\ndocument.body.append(\\n\\tO.el(count, count=> count%2\\n\\t\\t? el(\\\"p\\\", \\\"Last number is odd.\\\")\\n\\t\\t: el()\\n\\t),\\n\\tel(\\\"p\\\", \\\"Lucky numbers:\\\"),\\n\\tel(\\\"ul\\\").append(\\n\\t\\tO.el(numbers, numbers=> numbers.toReversed()\\n\\t\\t\\t.map(n=> el(\\\"li\\\", n)))\\n\\t)\\n);\\n\\nconst interval= 5*1000;\\nsetTimeout(clearInterval, 10*interval, setInterval(function(){\\n\\tO.action(count, \\\"add\\\");\\n\\tO.action(numbers, \\\"push\\\", count());\\n}, interval));\\n\"}],\"toolbar\":false}"));</script><div class="notice"><!--<dde:mark type="component" name="mnemonic" host="parentElement" ssr/>--><h3 id="h-mnemonic"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-mnemonic" tabindex="-1">#</a> Mnemonic</h3><ul><li><code>O(<value>)</code> — observable: reactive value</li><li><code>O(()=> <computation>)</code> — observable: reactive value dependent on calculation using other observables</li><li><code>O.on(<observable>, <listener>[, <options>])</code> — listen to the observable value changes</li><li><code>O.clear(...<observables>)</code> — off and clear observables</li><li><code>O(<value>, <actions>)</code> — observable: pattern to create complex reactive objects/arrays</li><li><code>O.action(<observable>, <action-name>, ...<action-arguments>)</code> — invoke an action for given observable</li><li><code>O.el(<observable>, <function-returning-dom>)</code> — render partial dom structure (template) based on the current observable value</li></ul></div><div class="prevNext"><!--<dde:mark type="component" name="prevNext" host="parentElement" ssr/>--><a rel="prev" href="p03-events" title="Using not only events in UI declaratively."><!--<dde:mark type="component" name="pageLink" host="parentElement" ssr/>-->Events and Addons (previous)</a><a rel="next" href="p05-scopes" title="Organizing UI into components"><!--<dde:mark type="component" name="pageLink" host="parentElement" ssr/>-->(next) Scopes and components</a></div></main></body></html>
 | 
			
		||||
</code></div><script>Flems(document.getElementById("code-example-2-8r8qappf8mo"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { O } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\nconst count= O(0, {\\n\\tadd(){ this.value= this.value + Math.round(Math.random()*10); }\\n});\\nconst numbers= O([ count() ], {\\n\\tpush(next){ this.value.push(next); }\\n});\\n\\nimport { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\ndocument.body.append(\\n\\tO.el(count, count=> count%2\\n\\t\\t? el(\\\"p\\\", \\\"Last number is odd.\\\")\\n\\t\\t: el()\\n\\t),\\n\\tel(\\\"p\\\", \\\"Lucky numbers:\\\"),\\n\\tel(\\\"ul\\\").append(\\n\\t\\tO.el(numbers, numbers=> numbers.toReversed()\\n\\t\\t\\t.map(n=> el(\\\"li\\\", n)))\\n\\t)\\n);\\n\\nconst interval= 5*1000;\\nsetTimeout(clearInterval, 10*interval, setInterval(function(){\\n\\tO.action(count, \\\"add\\\");\\n\\tO.action(numbers, \\\"push\\\", count());\\n}, interval));\\n\"}],\"toolbar\":false}"));</script><div class="notice"><!--<dde:mark type="component" name="mnemonic" host="parentElement" ssr/>--><h3 id="h-mnemonic"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-mnemonic" tabindex="-1">#</a> Mnemonic</h3><ul><li><code>O(<value>)</code> — observable: reactive value</li><li><code>O(()=> <computation>)</code> — read-only observable: reactive value dependent on calculation using other observables</li><li><code>O.on(<observable>, <listener>[, <options>])</code> — listen to the observable value changes</li><li><code>O.clear(...<observables>)</code> — off and clear observables</li><li><code>O(<value>, <actions>)</code> — observable: pattern to create complex reactive objects/arrays</li><li><code>O.action(<observable>, <action-name>, ...<action-arguments>)</code> — invoke an action for given observable</li><li><code>O.el(<observable>, <function-returning-dom>)</code> — render partial dom structure (template) based on the current observable value</li></ul></div><div class="prevNext"><!--<dde:mark type="component" name="prevNext" host="parentElement" ssr/>--><a rel="prev" href="p03-events" title="Using not only events in UI declaratively."><!--<dde:mark type="component" name="pageLink" host="parentElement" ssr/>-->Events and Addons (previous)</a><a rel="next" href="p05-scopes" title="Organizing UI into components"><!--<dde:mark type="component" name="pageLink" host="parentElement" ssr/>-->(next) Scopes and components</a></div></main></body></html>
 | 
			
		||||
@@ -12,26 +12,30 @@ export class CustomHTMLTestElement extends HTMLElement{
 | 
			
		||||
	}
 | 
			
		||||
	connectedCallback(){
 | 
			
		||||
		if(!this.hasAttribute("pre-name")) this.setAttribute("pre-name", "default");
 | 
			
		||||
		console.log(observedAttributes(this));
 | 
			
		||||
		this.attachShadow({ mode: "open" }).append(
 | 
			
		||||
			customElementRender(this, this.render)
 | 
			
		||||
			customElementRender(this, this.render, this.attributes)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render(test){
 | 
			
		||||
	attributes(element){
 | 
			
		||||
		const observed= O.observedAttributes(element);
 | 
			
		||||
		return Object.assign({ test: element.test }, observed);
 | 
			
		||||
	}
 | 
			
		||||
	render({ name, preName, test }){
 | 
			
		||||
		console.log(scope.state);
 | 
			
		||||
		scope.host(
 | 
			
		||||
			on.connected(()=> console.log(CustomHTMLTestElement)),
 | 
			
		||||
			on.attributeChanged(e=> console.log(e)),
 | 
			
		||||
			on.disconnected(()=> console.log(CustomHTMLTestElement))
 | 
			
		||||
		);
 | 
			
		||||
		const name= O.attribute("name");
 | 
			
		||||
		const preName= O.attribute("pre-name");
 | 
			
		||||
		
 | 
			
		||||
		console.log({ name, test, preName});
 | 
			
		||||
		const text= text=> el().append(
 | 
			
		||||
			el("#text", text),
 | 
			
		||||
			" | "
 | 
			
		||||
		);
 | 
			
		||||
		return el("p").append(
 | 
			
		||||
			el("#text", name),
 | 
			
		||||
			el("#text", preName),
 | 
			
		||||
			text(test),
 | 
			
		||||
			text(name),
 | 
			
		||||
			text(preName),
 | 
			
		||||
			el("button", { type: "button", textContent: "pre-name", onclick: ()=> preName("Ahoj") })
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "deka-dom-el",
 | 
			
		||||
	"version": "0.7.5",
 | 
			
		||||
	"version": "0.7.6",
 | 
			
		||||
	"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
 | 
			
		||||
	"author": "Jan Andrle <andrle.jan@centrum.cz>",
 | 
			
		||||
	"license": "MIT",
 | 
			
		||||
@@ -46,6 +46,7 @@
 | 
			
		||||
		"browser": true,
 | 
			
		||||
		"undef": "true",
 | 
			
		||||
		"latedef": "true",
 | 
			
		||||
		"-W014": true,
 | 
			
		||||
		"maxparams": 5,
 | 
			
		||||
		"maxdepth": 3,
 | 
			
		||||
		"maxcomplexity": 14,
 | 
			
		||||
@@ -58,7 +59,7 @@
 | 
			
		||||
	"size-limit": [
 | 
			
		||||
		{
 | 
			
		||||
			"path": "./index.js",
 | 
			
		||||
			"limit": "9.75 kB",
 | 
			
		||||
			"limit": "9.85 kB",
 | 
			
		||||
			"gzip": false,
 | 
			
		||||
			"brotli": false
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ export function customElementRender(custom_element, render, props= observedAttri
 | 
			
		||||
		host: (...c)=> c.length ? c.forEach(c=> c(custom_element)) : custom_element,
 | 
			
		||||
		custom_element
 | 
			
		||||
	});
 | 
			
		||||
	if(typeof props==="function") props= props(custom_element);
 | 
			
		||||
	if(typeof props==="function") props= props.call(custom_element, custom_element);
 | 
			
		||||
	const out= render.call(custom_element, props);
 | 
			
		||||
	scope.pop();
 | 
			
		||||
	return out;
 | 
			
		||||
@@ -33,20 +33,7 @@ function wrapMethod(obj, method, apply){
 | 
			
		||||
	obj[method]= new Proxy(obj[method] || (()=> {}), { apply });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function observedAttribute(instance, name){
 | 
			
		||||
	const out= (...args)=> !args.length
 | 
			
		||||
		? instance.getAttribute(name)
 | 
			
		||||
		: instance.setAttribute(name, ...args);
 | 
			
		||||
	out.attribute= name;
 | 
			
		||||
	return out;
 | 
			
		||||
}
 | 
			
		||||
import { observedAttributes as oA } from "./helpers.js";
 | 
			
		||||
export function observedAttributes(instance){
 | 
			
		||||
	const { observedAttributes= [] }= instance.constructor;
 | 
			
		||||
	return observedAttributes
 | 
			
		||||
		.reduce(function(out, name){
 | 
			
		||||
			Reflect.set(out, kebabToCamel(name), observedAttribute(instance, name));
 | 
			
		||||
			return out;
 | 
			
		||||
		}, {});
 | 
			
		||||
	;
 | 
			
		||||
	return oA(instance, (i, n)=> i.getAttribute(n));
 | 
			
		||||
}
 | 
			
		||||
function kebabToCamel(name){ return name.replace(/-./g, x=> x[1].toUpperCase()); }
 | 
			
		||||
 
 | 
			
		||||
@@ -84,6 +84,7 @@ on.attributeChanged= function(listener, options){
 | 
			
		||||
		});
 | 
			
		||||
		const c= onAbort(options.signal, ()=> observer.disconnect());
 | 
			
		||||
		if(c) observer.observe(element, { attributes: true });
 | 
			
		||||
		//TODO: clean up when element disconnected
 | 
			
		||||
		return element;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -15,3 +15,12 @@ export function onAbort(signal, listener){
 | 
			
		||||
		signal.removeEventListener("abort", listener);
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
export function observedAttributes(instance, observedAttribute){
 | 
			
		||||
	const { observedAttributes= [] }= instance.constructor;
 | 
			
		||||
	return observedAttributes
 | 
			
		||||
		.reduce(function(out, name){
 | 
			
		||||
			Reflect.set(out, kebabToCamel(name), observedAttribute(instance, name));
 | 
			
		||||
			return out;
 | 
			
		||||
		}, {});
 | 
			
		||||
}
 | 
			
		||||
function kebabToCamel(name){ return name.replace(/-./g, x=> x[1].toUpperCase()); }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ const stack_watch= [];
 | 
			
		||||
/**
 | 
			
		||||
 * ### `WeakMap<function, Set<ddeObservable<any, any>>>`
 | 
			
		||||
 * The `Set` is in the form of `[ source, ...depended observables (DSs) ]`.
 | 
			
		||||
 * When the DS is cleaned (`S.clear`) it is removed from DSs,
 | 
			
		||||
 * When the DS is cleaned (`O.clear`) it is removed from DSs,
 | 
			
		||||
 * if remains only one (`source`) it is cleared too.
 | 
			
		||||
 * ### `WeakMap<object, function>`
 | 
			
		||||
 * This is used for revesed deps, the `function` is also key for `deps`.
 | 
			
		||||
@@ -113,41 +113,38 @@ observable.el= function(o, map){
 | 
			
		||||
	return out;
 | 
			
		||||
};
 | 
			
		||||
import { on } from "./events.js";
 | 
			
		||||
import { observedAttributes } from "./helpers.js";
 | 
			
		||||
function observedAttribute(instance, name){
 | 
			
		||||
	const out= (...args)=> !args.length
 | 
			
		||||
		? instance.getAttribute(name)
 | 
			
		||||
		: instance.setAttribute(name, ...args);
 | 
			
		||||
	out.attribute= name;
 | 
			
		||||
	return out;
 | 
			
		||||
}
 | 
			
		||||
const key_attributes= "__dde_attributes";
 | 
			
		||||
observable.attribute= function(name, initial= null){
 | 
			
		||||
	//TODO host=element & reuse existing
 | 
			
		||||
	const out= observable(initial);
 | 
			
		||||
	let element;
 | 
			
		||||
	scope.host(el=> {
 | 
			
		||||
		element= el;
 | 
			
		||||
		if(elementAttribute(element, "has", name)) out(elementAttribute(element, "get", name));
 | 
			
		||||
		else if(initial!==null) elementAttribute(element, "set", name, initial);
 | 
			
		||||
		
 | 
			
		||||
		if(el[key_attributes]){
 | 
			
		||||
			el[key_attributes][name]= out;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		element[key_attributes]= { [name]: out };
 | 
			
		||||
		on.attributeChanged(function attributeChangeToObservable({ detail }){
 | 
			
		||||
			/*! This maps attributes to observables (`S.attribute`).
 | 
			
		||||
			 * Investigate `__dde_attributes` key of the element.*/
 | 
			
		||||
			const [ name, value ]= detail;
 | 
			
		||||
			const curr= element[key_attributes][name];
 | 
			
		||||
			if(curr) return curr(value);
 | 
			
		||||
		})(element);
 | 
			
		||||
		on.disconnected(function(){
 | 
			
		||||
			/*! This removes all observables mapped to attributes (`S.attribute`).
 | 
			
		||||
			 * Investigate `__dde_attributes` key of the element.*/
 | 
			
		||||
			observable.clear(...Object.values(element[key_attributes]));
 | 
			
		||||
		})(element);
 | 
			
		||||
	});
 | 
			
		||||
	return new Proxy(out, {
 | 
			
		||||
		apply(target, _, args){
 | 
			
		||||
			if(!args.length) return target();
 | 
			
		||||
			const value= args[0];
 | 
			
		||||
			return elementAttribute(element, "set", name, value);
 | 
			
		||||
		}
 | 
			
		||||
observable.observedAttributes= function(element){
 | 
			
		||||
	const attrs= observedAttributes(element, observedAttribute);
 | 
			
		||||
	const store= element[key_attributes]= {};
 | 
			
		||||
	const actions= {
 | 
			
		||||
		_set(value){ this.value= value; },
 | 
			
		||||
	};
 | 
			
		||||
	Object.keys(attrs).forEach(name=> {
 | 
			
		||||
		const attr= attrs[name]= toObservable(attrs[name], attrs[name](), actions);
 | 
			
		||||
		store[attr.attribute]= attr;
 | 
			
		||||
	});
 | 
			
		||||
	on.attributeChanged(function attributeChangeToObservable({ detail }){
 | 
			
		||||
		/*! This maps attributes to observables (`O.observedAttributes`).
 | 
			
		||||
			* Investigate `__dde_attributes` key of the element.*/
 | 
			
		||||
		const [ name, value ]= detail;
 | 
			
		||||
		const curr= element[key_attributes][name];
 | 
			
		||||
		if(curr) return observable.action(curr, "_set", value);
 | 
			
		||||
	})(element);
 | 
			
		||||
	on.disconnected(function(){
 | 
			
		||||
		/*! This removes all observables mapped to attributes (`O.observedAttributes`).
 | 
			
		||||
			* Investigate `__dde_attributes` key of the element.*/
 | 
			
		||||
		observable.clear(...Object.values(element[key_attributes]));
 | 
			
		||||
	})(element);
 | 
			
		||||
	return attrs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
import { typeOf } from './helpers.js';
 | 
			
		||||
@@ -169,7 +166,7 @@ function removeObservablesFromElements(o, listener, ...notes){
 | 
			
		||||
			element[key_reactive]= [];
 | 
			
		||||
			on.disconnected(()=>
 | 
			
		||||
				/*!
 | 
			
		||||
				 * Clears all Observables listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
			
		||||
				 * Clears all Observables listeners added in the current scope/host (`O.el`, `assign`, …?).
 | 
			
		||||
				 * You can investigate the `__dde_reactive` key of the element.
 | 
			
		||||
				 * */
 | 
			
		||||
				element[key_reactive].forEach(([ [ o, listener ] ])=>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user