This is the closest I can come to emulating the DOM declaration of an event handler. This code does everything, from what I can tell, that the DOM does for built in event handlers.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>On-Event Test</title>
<script>
function onCheckHandler(event) {
console.log('onCheckHandler: %O', event);
event.stopPropagation();
event.stopImmediatePropagation();
//event.preventDefault();
}
function eventListener(event) {
console.log('eventListener: %O', event);
}
(function() {
var template = `<div>Click to Check</div>`;
class MyEl extends HTMLElement {
constructor() {
super();
var rootEl = this.attachShadow({mode: 'open'});
rootEl.innerHTML = template;
rootEl.firstChild.addEventListener('click', () => {
var checkEvent = new CustomEvent("check", {bubbles:true,cancelable:true});
if (this.dispatchEvent(checkEvent)) {
// Do default operation here
console.log('Performing default operation');
}
});
this._onCheckFn = null;
}
static get observedAttributes() {
return ['oncheck'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (attrName === 'oncheck' && oldVal !== newVal) {
if (newVal === null) {
this.oncheck = null;
}
else {
this.oncheck = Function(`return function oncheck(event) {\n\t${newVal};\n};`)();
}
}
}
get oncheck() {
return this._onCheckFn;
}
set oncheck(handler) {
if (this._onCheckFn) {
this.removeEventListener('check', this._onCheckFn);
this._onCheckFn = null;
}
if (typeof handler === 'function') {
this._onCheckFn = handler;
this.addEventListener('check', this._onCheckFn);
}
}
}
// Define our web component
customElements.define('my-el', MyEl);
})();
</script>
</head>
<body>
<my-el oncheck="onCheckHandler(event)"></my-el>
</body>
</html>
To get this to work you need two steps:
Step One:
The component code must support an attribute change.
When the attribute is set to a string (The function to call) then the code creates a temporary function that attempts to call the function provided as the attribute value. That function is then passed on to step two.
When the attribute is set to anything else or the attribute is removed then the code passes a null on to step two.
Step Two:
The component code must support the oncheck
property.
Whenever this property is changed it needs to remove any previously defined event handler for this property by calling removeEventListener
.
If the new value of the property is a function then is calls addEventListener
with that function as the handler.
This was fun to figure out. At first I thought that the DOM based oncheck
handler would always be first. But by testing onclick
and addEventHandler('click')
I discovered that the event handlers are added in the order received. If onclick
is provided in the DOM than that event listener is added first. If you then call removeAttribute('onclick')
and then setAttribute('onclick', 'handler(event)')
then that event handler is moved to the end of the list.
So this code replicates exactly what I same with the click
event.