the home site for me: also iteration 3 or 4 of my site
1document.addEventListener("DOMContentLoaded", () => {
2 const content = document.querySelector("main");
3 if (!content) return;
4
5 const walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT, {
6 acceptNode: (node) => {
7 // Skip code blocks, pre tags, and script/style tags
8 let parent = node.parentElement;
9 while (parent) {
10 const tag = parent.tagName.toLowerCase();
11 if (
12 tag === "code" ||
13 tag === "pre" ||
14 tag === "script" ||
15 tag === "style"
16 ) {
17 return NodeFilter.FILTER_REJECT;
18 }
19 parent = parent.parentElement;
20 }
21 return NodeFilter.FILTER_ACCEPT;
22 },
23 });
24
25 const nodesToReplace = [];
26 while (walker.nextNode()) {
27 const node = walker.currentNode;
28 if (/:[\w-]+:/.test(node.textContent)) {
29 nodesToReplace.push(node);
30 }
31 }
32
33 nodesToReplace.forEach((node) => {
34 const frag = document.createDocumentFragment();
35 const parts = node.textContent.split(/(:[\w-]+:)/);
36
37 parts.forEach((part) => {
38 if (/^:[\w-]+:$/.test(part)) {
39 const name = part.slice(1, -1);
40
41 const span = document.createElement("span");
42 span.className = "emoji-inline--wrapper";
43
44 const img = document.createElement("img");
45 img.src = `https://cachet.dunkirk.sh/emojis/${name}/r`;
46 img.alt = part;
47 img.className = "emoji-inline";
48 img.loading = "lazy";
49 img.setAttribute("aria-label", `${name} emoji`);
50
51 // Fallback: if image fails to load, show original text
52 img.onerror = () => {
53 span.replaceWith(document.createTextNode(part));
54 };
55
56 span.appendChild(img);
57 frag.appendChild(span);
58 } else if (part) {
59 frag.appendChild(document.createTextNode(part));
60 }
61 });
62
63 node.replaceWith(frag);
64 });
65});