Skip to content

Commit a7e5de1

Browse files
committed
feat(core dom): Add dom.element_uuid to get/set an uuid to a DOM node.
1 parent 2f95379 commit a7e5de1

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

src/core/dom.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,34 @@ const escape_css_id = (id) => {
529529
return `#${CSS.escape(id.split("#")[1])}`;
530530
};
531531

532+
/**
533+
* Get a universally unique id (uuid) for a DOM element.
534+
*
535+
* This method returns a uuid for the given element. On the first call it will
536+
* generate a uuid and store it on the element.
537+
*
538+
* @param {Node} el - The DOM node to get the uuid for.
539+
* @returns {String} - The uuid.
540+
*/
541+
const element_uuid = (el) => {
542+
if (!get_data(el, "uuid", false)) {
543+
let uuid;
544+
if (window.crypto.randomUUID) {
545+
// Create a real UUID
546+
// window.crypto.randomUUID does only exist in browsers with secure
547+
// context.
548+
// See: https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID
549+
uuid = window.crypto.randomUUID();
550+
} else {
551+
// Create a sufficiently unique ID
552+
const array = new Uint32Array(4);
553+
uuid = window.crypto.getRandomValues(array).join("");
554+
}
555+
set_data(el, "uuid", uuid);
556+
}
557+
return get_data(el, "uuid");
558+
};
559+
532560
const dom = {
533561
toNodeArray: toNodeArray,
534562
querySelectorAllAndMe: querySelectorAllAndMe,
@@ -556,6 +584,7 @@ const dom = {
556584
template: template,
557585
get_visible_ratio: get_visible_ratio,
558586
escape_css_id: escape_css_id,
587+
element_uuid: element_uuid,
559588
add_event_listener: events.add_event_listener, // BBB export. TODO: Remove in an upcoming version.
560589
remove_event_listener: events.remove_event_listener, // BBB export. TODO: Remove in an upcoming version.
561590
};

src/core/dom.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,3 +874,32 @@ describe("escape_css_id", function () {
874874
expect(dom.escape_css_id("#-1-2-3")).toBe("#-\\31 -2-3");
875875
});
876876
});
877+
878+
describe("element_uuid", function () {
879+
it("returns a UUIDv4 for an element", function () {
880+
const el = document.createElement("div");
881+
const uuid = dom.element_uuid(el);
882+
expect(uuid).toMatch(
883+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
884+
);
885+
886+
// The UUID isn't created anew when called again.
887+
expect(dom.element_uuid(el)).toBe(uuid);
888+
});
889+
890+
it("returns a sufficiently unique id for an element", function () {
891+
// Mock window.crypto.randomUUID not existing, like in browser with
892+
// non-secure context.
893+
const orig_randomUUID = window.crypto.randomUUID;
894+
window.crypto.randomUUID = undefined;
895+
896+
const el = document.createElement("div");
897+
const uuid = dom.element_uuid(el);
898+
expect(uuid).toMatch(/^[0-9]*$/);
899+
900+
// The UUID isn't created anew when called again.
901+
expect(dom.element_uuid(el)).toBe(uuid);
902+
903+
window.crypto.randomUUID = orig_randomUUID;
904+
});
905+
});

src/setup-tests.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ dom.is_visible = (el) => {
2222

2323
// polyfill css.escape for jsdom
2424
import("css.escape");
25+
26+
// NodeJS polyfill for window.crypto.randomUUID
27+
import crypto from "crypto";
28+
window.crypto.randomUUID = () => crypto.randomUUID();

0 commit comments

Comments
 (0)