diff options
| author | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-06 23:46:53 +0300 |
|---|---|---|
| committer | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-06 23:46:53 +0300 |
| commit | 57e2d2be666f4daca0b19e4f7230d9130713a198 (patch) | |
| tree | 1240493ad250c7f4db8e9173d73447a633571d5e | |
| parent | d1e5b5126af907215708fde817dcd309e579b725 (diff) | |
| download | vyos-documentation-57e2d2be666f4daca0b19e4f7230d9130713a198.tar.gz vyos-documentation-57e2d2be666f4daca0b19e4f7230d9130713a198.zip | |
fix(codecopier): guard UI updates against out-of-order async completions
Rapid re-clicks queue multiple `navigator.clipboard.writeText`
promises; without a request token, an older promise resolving later
can overwrite the newer click's UI (or have its 2-second timeout fire
on the new click's state). Add a per-button `copyRequestId` that
each click increments and captures locally; the success/failure UI
flip and the revert-timeout body all bail out early if the captured
token no longer matches the current one.
Addresses CodeRabbit review feedback on PR #1890.
\xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
| -rw-r--r-- | docs/_static/js/codecopier.js | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/docs/_static/js/codecopier.js b/docs/_static/js/codecopier.js index 02143b92..4b0941c9 100644 --- a/docs/_static/js/codecopier.js +++ b/docs/_static/js/codecopier.js @@ -54,6 +54,16 @@ $(document).ready(async function () { }) => { const id = currentTarget.dataset.identifier const divWithNeededId = $(`div[data-identifier='${id}']`) + + // Per-click request token. Rapid re-clicks queue multiple writeText + // promises; without this guard, an older promise resolving later + // can overwrite the newer click's UI (or have its timeout fire on + // the new state). Each click increments the token, captures the + // local copy, and bails out of any UI/timeout work if the captured + // token no longer matches the current one. + const requestId = (divWithNeededId.data('copyRequestId') || 0) + 1 + divWithNeededId.data('copyRequestId', requestId) + // Read from the .copyDiv's parentElement (the inner .highlight div it was // injected into via insertAdjacentHTML('beforeend', ...)) rather than // currentTarget.offsetParent — offsetParent depends on CSS positioning @@ -75,17 +85,20 @@ $(document).ready(async function () { try { await navigator.clipboard.writeText(textToCopy) + if (divWithNeededId.data('copyRequestId') !== requestId) return // Only flip the button into the success state when the write actually // resolves — earlier versions showed "Copied!" even on failure. divWithNeededId.addClass('copiedNotifier') divWithNeededId.html('<span>Copied!</span>') } catch (error) { + if (divWithNeededId.data('copyRequestId') !== requestId) return console.error('Copying text failed, please try again', error) divWithNeededId.addClass('copyFailedNotifier') divWithNeededId.html('<span>Failed</span>') } const revertTimeout = setTimeout(() => { + if (divWithNeededId.data('copyRequestId') !== requestId) return divWithNeededId.html(innersOfCopyDiv) divWithNeededId.removeClass('copiedNotifier copyFailedNotifier') divWithNeededId.removeData('revertTimeout') |
