Skip to content

Commit 737fa7f

Browse files
committed
Fix overlapping site copy-button feedback timers
Use one generation token and a shared restore timer so a second click cannot let an older timeout wipe newer Copied feedback.
1 parent 653365a commit 737fa7f

2 files changed

Lines changed: 47 additions & 13 deletions

File tree

web/static/script.js

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,48 @@
7878
copyStatus.textContent = message;
7979
};
8080

81+
// One shared restore timer + generation token so rapid clicks (same button
82+
// or another copy control) cannot let an older timeout wipe newer feedback.
83+
var copyFeedbackToken = 0;
84+
var copyFeedbackTimer = null;
85+
var activeCopyButton = null;
86+
87+
var restoreCopyButton = function (button) {
88+
button.textContent = "Copy";
89+
button.classList.remove("is-copied");
90+
};
91+
92+
var clearCopyFeedbackTimer = function () {
93+
if (copyFeedbackTimer) {
94+
clearTimeout(copyFeedbackTimer);
95+
copyFeedbackTimer = null;
96+
}
97+
};
98+
99+
var showCopyFeedback = function (button, label) {
100+
if (activeCopyButton && activeCopyButton !== button) {
101+
restoreCopyButton(activeCopyButton);
102+
}
103+
activeCopyButton = button;
104+
button.textContent = label;
105+
if (label === "Copied") {
106+
button.classList.add("is-copied");
107+
} else {
108+
button.classList.remove("is-copied");
109+
}
110+
announceCopy(label);
111+
112+
clearCopyFeedbackTimer();
113+
var token = ++copyFeedbackToken;
114+
copyFeedbackTimer = setTimeout(function () {
115+
if (token !== copyFeedbackToken) { return; }
116+
restoreCopyButton(button);
117+
if (activeCopyButton === button) { activeCopyButton = null; }
118+
announceCopy("");
119+
copyFeedbackTimer = null;
120+
}, 2000);
121+
};
122+
81123
copyBlocks.forEach(function (block) {
82124
var source = block.querySelector("code");
83125
if (!source || !canCopy) { return; }
@@ -97,23 +139,12 @@
97139
.join("")
98140
.trim();
99141

100-
var restore = function () {
101-
button.textContent = "Copy";
102-
button.classList.remove("is-copied");
103-
announceCopy("");
104-
};
105-
106142
navigator.clipboard.writeText(text).then(
107143
function () {
108-
button.textContent = "Copied";
109-
button.classList.add("is-copied");
110-
announceCopy("Copied");
111-
setTimeout(restore, 2000);
144+
showCopyFeedback(button, "Copied");
112145
},
113146
function () {
114-
button.textContent = "Press ⌘C";
115-
announceCopy("Press ⌘C");
116-
setTimeout(restore, 2000);
147+
showCopyFeedback(button, "Press ⌘C");
117148
}
118149
);
119150
});

web/tests/site.test.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ test("keeps the site-audit copy and a11y fixes", async () => {
189189

190190
assert.match(script, /aria-live", "polite"/);
191191
assert.match(script, /announceCopy\("Copied"\)/);
192+
assert.match(script, /copyFeedbackToken/);
193+
assert.match(script, /clearTimeout\(copyFeedbackTimer\)/);
194+
assert.match(script, /showCopyFeedback/);
192195
assert.match(script, /announceCopy\("Press C"\)/);
193196

194197
const ogSvg = await readFile(join(siteRoot, "static/og-image.svg"), "utf8");

0 commit comments

Comments
 (0)