<div class="wrapper">
<div class="left">
<div class="block" id="block-1">
<div class="block" id="block-2">
<div class="block" id="block-3">
<div class="block" id="block-4">
<div class="block" id="block-5">
<div class="block" id="block-6">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<form class="right" id="settings-form">
<div class="settings">
<div>
Dispatch on:
<div>
<input type="radio" name="target" id="target-block-1" value="block-1">
<label for="target-block-1">#block-1</label>
</div>
<div>
<input type="radio" name="target" id="target-block-2" value="block-2a">
<label for="target-block-2">#block-2</label>
</div>
<div>
<input type="radio" name="target" id="target-block-3" value="block-3">
<label for="target-block-3">#block-3</label>
</div>
<div>
<input type="radio" name="target" id="target-block-4" value="block-4">
<label for="target-block-4">#block-4</label>
</div>
<div>
<input type="radio" name="target" id="target-block-5" value="block-5" checked>
<label for="target-block-5">#block-5</label>
</div>
<div>
<input type="radio" name="target" id="target-block-6" value="block-6">
<label for="target-block-6">#block-6</label>
</div>
<div> <hr ></div>
<div>
<input type="checkbox" name="bubbles" id="bubbles" value="true" checked>
<label for="bubbles">Bubbles</label>
</div>
</div>
<div>
Capture on:
<div>
<input type="checkbox" name="capture" id="capture-block-1" value="block-1" checked>
<label for="capture-block-1">#block-1</label>
</div>
<div>
<input type="checkbox" name="capture" id="capture-block-2" value="block-2" checked>
<label for="capture-block-2">#block-2</label>
</div>
<div>
<input type="checkbox" name="capture" id="capture-block-3" value="block-3" checked>
<label for="capture-block-3">#block-3</label>
</div>
<div>
<input type="checkbox" name="capture" id="capture-block-4" value="block-4" checked>
<label for="capture-block-4">#block-4</label>
</div>
<div>
<input type="checkbox" name="capture" id="capture-block-5" value="block-5" checked>
<label for="capture-block-5">#block-5</label>
</div>
<div>
<input type="checkbox" name="capture" id="capture-block-6" value="block-6" checked>
<label for="capture-block-6">#block-6</label>
</div>
</div>
<div>
Stop propagation on:
<div>
<input type="radio" name="stopPropagation" id="stopPropagation-block-1" value="block-1">
<label for="stopPropagation-block-1">#block-1</label>
</div>
<div>
<input type="radio" name="stopPropagation" id="stopPropagation-block-2" value="block-2">
<label for="stopPropagation-block-2">#block-2</label>
</div>
<div>
<input type="radio" name="stopPropagation" id="stopPropagation-block-3" value="block-3">
<label for="stopPropagation-block-3">#block-3</label>
</div>
<div>
<input type="radio" name="stopPropagation" id="stopPropagation-block-4" value="block-4">
<label for="stopPropagation-block-4">#block-4</label>
</div>
<div>
<input type="radio" name="stopPropagation" id="stopPropagation-block-5" value="block-5">
<label for="stopPropagation-block-5">#block-5</label>
</div>
<div>
<input type="radio" name="stopPropagation" id="stopPropagation-block-6" value="block-6">
<label for="stopPropagation-block-6">#block-6</label>
</div>
<div>
<input type="radio" name="stopPropagation" id="stopPropagation-none" value="none" checked>
<label for="stopPropagation-none">none</label>
</div>
</div>
</div>
<div class="actions">
<button type="submit">Dispatch event</button>
<button id="reset">Reset</button>
</div>
<div class="legend">
<div class="legend-item">
<div class="legend-rect"></div>
NONE
</div>
<div class="legend-item capture">
<div class="legend-rect"></div>
CAPTURING_PHASE
</div>
<div class="legend-item target">
<div class="legend-rect"></div>
AT_TARGET
</div>
<div class="legend-item bubble">
<div class="legend-rect"></div>
BUBBLING_PHASE
</div>
</div>
</form>
</div>
.wrapper {
display: flex;
justify-content: space-between;
}
.left, .right {
flex: 1 1 auto;
}
.settings {
display: flex;
gap: 20px;
}
.actions {
display: flex;
gap: 20px;
margin-top: 50px;
}
.block {
position: relative;
padding: 10px 0 10px 20px;
color: #555;
&::before {
content: '<div class="block">';
}
&::after {
content: '</div>';
display: block;
}
}
.legend {
margin-top: 50px;
}
.legend-item {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 10px;
color: #555;
}
.legend-rect {
width: 40px;
height: 15px;
border: 2px solid #555;
background: rgba(150, 150, 150, 0.5);
}
.legend-item.target .legend-rect {
border-color: rgb(200, 0, 0);
background-color: rgba(200, 0, 0, 0.3);
}
.legend-item.capture .legend-rect {
border-color: rgb(0, 150, 0);
background-color: rgba(0, 150, 0, 0.3);
}
.legend-item.bubble .legend-rect {
border-color: rgb(0, 0, 200);
background-color: rgba(0, 0, 200, 0.3);
}
.target {
color: rgb(200, 0, 0);
}
.capture {
color: rgb(0, 150, 0);
}
.bubble {
color: rgb(0, 0, 200);
}
.num {
position: absolute;
top: 10px;
left: 5px;
}
#block-1 {
&::before {
content: '<div id="block-1">';
}
}
#block-2 {
&::before {
content: '<div id="block-2">';
}
}
#block-3 {
&::before {
content: '<div id="block-3">';
}
}
#block-4 {
&::before {
content: '<div id="block-4">';
}
}
#block-5 {
&::before {
content: '<div id="block-5">';
}
}
#block-6 {
&::before {
content: '<div id="block-6">';
}
}
const PHASES = [
"NONE",
"CAPTURING_PHASE",
"AT_TARGET",
"BUBBLING_PHASE",
]
const blocks = [
document.getElementById("block-1"),
document.getElementById("block-2"),
document.getElementById("block-3"),
document.getElementById("block-4"),
document.getElementById("block-5"),
document.getElementById("block-6"),
];
let count = 0;
let target;
let bubble;
let capture;
let stopPropagation;
const form = document.getElementById("settings-form");
form.addEventListener("submit", (event) => {
event.preventDefault();
const settings = new FormData(event.target);
target = settings.get("target");
bubbles = !!settings.get("bubbles");
capture = settings.getAll("capture");
stopPropagation = settings.get("stopPropagation");
reset();
blocks.forEach((block) => {
block.addEventListener("test", callback, { capture: capture.includes(block.getAttribute("id")) });
});
document.getElementById(target).dispatchEvent(new Event("test", { bubbles }));
});
document.getElementById("reset").addEventListener("click", (event) => {
event.preventDefault();
reset();
form.querySelectorAll("[name='target']").forEach((element) => {
element.checked = element.value === "block-5";
});
form.querySelector("[name='bubbles']").checked = true;
form.querySelectorAll("[name='capture']").forEach((element) => {
element.checked = true;
});
form.querySelectorAll("[name='stopPropagation']").forEach((element) => {
element.checked = element.value === "none";
});
})
function reset() {
count = 0;
document.querySelectorAll(".num").forEach((num) => {
num.remove();
});
blocks.forEach(block => {
block.removeEventListener("test", callback, { capture: true });
block.removeEventListener("test", callback, { capture: false });
block.classList.remove("target", "capture", "bubble");
})
}
function callback(event) {
const target = event.currentTarget;
const phase = PHASES[event.eventPhase];
const shouldStop = stopPropagation.includes(target.getAttribute("id"));
const cnt = count;
if (shouldStop) {
event.stopPropagation();
}
setTimeout(() => {
switch (phase) {
case "AT_TARGET":
target.classList.add("target");
break;
case "CAPTURING_PHASE":
target.classList.add("capture");
break;
case "BUBBLING_PHASE":
target.classList.add("bubble");
break;
}
const num = document.createElement("div");
num.classList = "num";
num.innerHTML = cnt + 1;
target.appendChild(num);
}, ++count * 1000);
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.