joicomponents
Version:
Design patterns to build native web components
83 lines (75 loc) • 5.01 kB
HTML
<script>
window.addEventListener("click",e => {console.log("click", "capture", "window (before link-click script)");}, true);
</script>
<div id="a">
<a id="b" href="#b">#b <span id="c">(inside #c)</span></a>
<hr>
<a href="#d" id="d">#d calls stopTrailingClickEvent()</a>
<hr>
<a href="#e" id="e">#e calls stopPropagation()</a>
<hr> -----
</div>
<a href="https://bbc.com" id="f">bbc.com (will navigate out)</a>
<script>
//link-click event starts here
//https://www.w3.org/html/wg/spec/content-models.html#interactive-content-0
//http://qaru.site/questions/10726/can-i-nest-a-button-element-inside-an-a-using-html5
//todo make this into a discussion in the chapter, explain how the default action of the click event
//todo is not hindered when interactive content is illegally put inside an <a href> tag and still
//todo displayed in the browser.
//https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission
function filterLinkClicks(e) {
if (e.metaKey)
return;
for (let el = e.target; el; el = el.parentNode) {
// if (elementCannotBeInALink(el)) return; cannot be used here, as the default action of the event is not filtered for illegal composition in the DOM that the browser still chooses to render.
//
if (el.nodeName === "A" || el.nodeName === "a" || el.nodeName === "AREA"){
const linkClick = new CustomEvent("link-click", {bubbles: true, composed: true});
//wrap preventDefault in PriorEvent
linkClick.preventDefault = function(){e.preventDefault();}
linkClick.stopTrailingClickEvent = function(){e.stopImmediatePropagation ? e.stopImmediatePropagation() : e.stopPropagation();}
return el.dispatchEvent(linkClick);
}
}
}
(window || document).addEventListener("click", e => filterLinkClicks(e), true);
//link-click event ends here
//test of link-click event
let a = document.getElementById("a");
let b = document.getElementById("b");
let c = document.getElementById("c");
let d = document.getElementById("d");
let w = window;
w.addEventListener("click",e => {console.log("click", "capture", "window");},{capture: true});
a.addEventListener("click", e => {console.log("click", "capture", "a");},{capture: true});
b.addEventListener("click", e => {console.log("click", "capture", "b");},{capture: true});
c.addEventListener("click", e => {console.log("click", "capture", "c");},{capture: true});
d.addEventListener("click", e => {console.log("click", "capture", "d");},{capture: true});
e.addEventListener("click", e => {console.log("click", "capture", "e");},{capture: true});
f.addEventListener("click", e => {console.log("click", "capture", "f");},{capture: true});
a.addEventListener("click", e => {console.log("click", "bubble", "a");});
b.addEventListener("click", e => {console.log("click", "bubble", "b");});
c.addEventListener("click", e => {console.log("click", "bubble", "c");});
d.addEventListener("click", e => {console.log("click", "bubble", "d");});
e.addEventListener("click", e => {console.log("click", "bubble", "e");});
f.addEventListener("click", e => {console.log("click", "bubble", "f");});
w.addEventListener("click",e => {console.log("click", "bubble", "window");});
w.addEventListener("link-click",e => {console.log("link-click", "capture", "window");},{capture: true});
a.addEventListener("link-click", e => {console.log("link-click", "capture", "a");},{capture: true});
b.addEventListener("link-click", e => {console.log("link-click", "capture", "b");},{capture: true});
c.addEventListener("link-click", e => {console.log("link-click", "capture", "c");},{capture: true});
d.addEventListener("link-click", e => {console.log("link-click", "capture", "d")},{capture: true});
e.addEventListener("link-click", e => {console.log("link-click", "capture", "e")},{capture: true});
f.addEventListener("link-click", e => {console.log("link-click", "capture", "f")},{capture: true});
a.addEventListener("link-click", e => {console.log("link-click", "bubble", "a");});
b.addEventListener("link-click", e => {console.log("link-click", "bubble", "b");});
c.addEventListener("link-click", e => {console.log("link-click", "bubble", "c");});
d.addEventListener("link-click", e => {console.log("link-click", "bubble", "d");});
e.addEventListener("link-click", e => {console.log("link-click", "bubble", "e");});
f.addEventListener("link-click", e => {console.log("link-click", "bubble", "f");});
w.addEventListener("link-click",e => {console.log("link-click", "bubble", "window");});
a.addEventListener("link-click",e => {e.preventDefault();console.log("link-click", "preventDefault", "w bubble");});
d.addEventListener("link-click", e => {console.log("click", "stopTrailingClickEvent", "d target");e.stopTrailingClickEvent();});
e.addEventListener("link-click", e => {console.log("click", "stopImmediatePropagation", "e target"); e.stopImmediatePropagation();});
</script>