Skip to content

Commit 57fe77d

Browse files
committed
d
1 parent 2b72d3d commit 57fe77d

2 files changed

Lines changed: 137 additions & 3 deletions

File tree

pages/html/handleRadio.html

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>handleRadio Demo</title>
7+
</head>
8+
<body>
9+
<h1>handleRadio Demo</h1>
10+
11+
<div id="radio-container">
12+
<label> <input type="radio" name="theme" value="light" /> Light </label>
13+
<label> <input type="radio" name="theme" value="dark" /> Dark </label>
14+
<label> <input type="radio" name="theme" value="system" /> System </label>
15+
</div>
16+
17+
<hr />
18+
19+
<div><strong>Selected Value:</strong> <span id="current-value">None</span></div>
20+
21+
<hr />
22+
23+
<button id="btn-dark">Select Dark via Code</button>
24+
<button id="btn-unbind">Unbind (Stop Listening)</button>
25+
<button id="btn-bind">Bind (Start Listening)</button>
26+
<span>status: <span id="status">idle</span></span>
27+
28+
<hr />
29+
30+
<h3>Tool.list output:</h3>
31+
<pre id="list-output"></pre>
32+
33+
<script type="module">
34+
import handleRadio from "./handleRadio.js";
35+
36+
const display = document.getElementById("current-value");
37+
const listOutput = document.getElementById("list-output");
38+
const container = document.getElementById("radio-container");
39+
40+
const tool = handleRadio({
41+
name: "theme",
42+
delegateParent: container,
43+
initState: "system",
44+
initTrigger: true,
45+
autoBind: false, // this will tell not to bind automatically
46+
onChange: (value, tool) => {
47+
display.innerText = value;
48+
console.log("Changed to:", value);
49+
50+
// Update the list output to show the live state
51+
listOutput.innerText = JSON.stringify(
52+
tool.list.map((i) => ({ value: i.value, checked: i.el.checked })),
53+
null,
54+
2
55+
);
56+
},
57+
});
58+
59+
document.getElementById("btn-dark").addEventListener("click", () => {
60+
tool.checkByValue("dark");
61+
// Note: tool.checkByValue just sets checked = true,
62+
// it doesn't trigger a click event, so we might need to manually update display if needed,
63+
// OR the browser might trigger it depending on implementation.
64+
// In handleRadio.js, the listener is on 'click'. checkByValue doesn't trigger 'click'.
65+
// However, usually programmatic changes don't trigger click.
66+
// Let's see if we should trigger onChange manually or if the user expects it.
67+
// handleRadio.js doesn't seem to have a way to trigger onChange programmatically besides initTrigger.
68+
69+
// Re-render list to show change
70+
listOutput.innerText = JSON.stringify(
71+
tool.list.map((i) => ({ value: i.value, checked: i.el.checked })),
72+
null,
73+
2
74+
);
75+
// Since checkByValue doesn't trigger click, we manually update display here for the demo
76+
display.innerText = "dark (set via code)";
77+
});
78+
79+
document.getElementById("btn-unbind").addEventListener("click", () => {
80+
tool.unbind();
81+
alert("Unbound! Clicking radios won't update the display anymore.");
82+
});
83+
84+
document.getElementById("btn-bind").addEventListener("click", () => {
85+
tool.bind();
86+
alert("Bound! Clicking radios will now update the display.");
87+
});
88+
</script>
89+
90+
<hr />
91+
92+
<section id="explanation">
93+
<h2>What we are trying to achieve</h2>
94+
<p>
95+
This page demonstrates the usage of the <code>handleRadio.js</code> utility, which is a lightweight tool for
96+
managing groups of radio buttons using <strong>event delegation</strong>.
97+
</p>
98+
<ul>
99+
<li>
100+
<strong>Efficiency:</strong> Instead of attaching listeners to every radio button, it attaches a single
101+
listener to a parent element (or the <code>body</code>).
102+
</li>
103+
<li>
104+
<strong>State Management:</strong> It can initialize the radio group with a specific value and ensures that at
105+
least one radio is always selected if possible.
106+
</li>
107+
<li>
108+
<strong>Programmatic Control:</strong> It returns a tool object that allows you to check radios by value, list
109+
all radios in the group with their current states, or unbind the event listener when no longer needed.
110+
</li>
111+
<li>
112+
<strong>Decoupling:</strong> The <code>onChange</code> callback allows you to react to user interactions in a
113+
centralized way.
114+
</li>
115+
</ul>
116+
</section>
117+
</body>
118+
</html>

pages/html/handleRadio.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111
export default function handleRadio(opt) {
1212
let tool;
13+
1314
const {
1415
name,
1516
selectorAll = (name) => {
@@ -21,6 +22,7 @@ export default function handleRadio(opt) {
2122
},
2223
initState,
2324
initTrigger = false,
25+
autoBind = true,
2426
} = opt;
2527

2628
let { delegateParent, onChange } = opt;
@@ -41,6 +43,7 @@ export default function handleRadio(opt) {
4143

4244
if (initState) {
4345
const sOne = selectorOne(name, initState);
46+
4447
const el = delegateParent.querySelector(sOne);
4548

4649
if (el) {
@@ -52,6 +55,7 @@ export default function handleRadio(opt) {
5255

5356
if (!active) {
5457
active = delegateParent.querySelector(s);
58+
5559
if (active) {
5660
active.checked = true;
5761
}
@@ -64,26 +68,37 @@ export default function handleRadio(opt) {
6468

6569
if (match) {
6670
const v = target.value;
71+
6772
onChange(v, tool);
6873
}
6974
};
70-
delegateParent.addEventListener("click", delegateFn);
75+
76+
if (autoBind) {
77+
delegateParent.addEventListener("click", delegateFn);
78+
}
7179

7280
tool = {
7381
unbind: () => {
7482
delegateParent.removeEventListener("click", delegateFn);
7583
},
84+
bind: () => {
85+
delegateParent.removeEventListener("click", delegateFn); // Prevent duplicates
86+
87+
delegateParent.addEventListener("click", delegateFn);
88+
},
7689
list: [...delegateParent.querySelectorAll(s)].reduce((acc, el) => {
7790
acc.push({
7891
el,
7992
checked: el.checked,
8093
value: el.value,
8194
});
95+
8296
return acc;
8397
}, []),
8498
checkByValue: (value) => {
85-
const sOne = selectorOne(name, value);
86-
const el = delegateParent.querySelector(sOne);
99+
const selector = selectorOne(name, value);
100+
101+
const el = delegateParent.querySelector(selector);
87102

88103
if (el) {
89104
el.checked = true;
@@ -93,6 +108,7 @@ export default function handleRadio(opt) {
93108

94109
if (initTrigger) {
95110
const activeEl = delegateParent.querySelector(`${s}:checked`);
111+
96112
if (activeEl) {
97113
onChange(activeEl.value, tool);
98114
}

0 commit comments

Comments
 (0)