Skip to content

Commit eb02a13

Browse files
committed
chore: speed up mutationobserver (#923)
1 parent 82606c3 commit eb02a13

File tree

7 files changed

+453
-452
lines changed

7 files changed

+453
-452
lines changed

docs/scripts/faq.js

Lines changed: 41 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,50 @@
1-
function initializeFAQ(faqSection) {
2-
console.log("[Initialize] FAQ", faqSection?.id || "all");
3-
4-
// If no section provided, fall back to querying all accordions
5-
const accordions = faqSection ?
6-
faqSection.querySelectorAll('.faq-accordion') :
7-
document.querySelectorAll('.faq-accordion');
1+
function initializeFAQ() {
2+
// Find all FAQ sections that need initialization
3+
document.querySelectorAll('.faq-section:not([data-faq-initialized])').forEach(faqSection => {
4+
// Skip if already initialized
5+
if (faqSection.hasAttribute('data-faq-initialized')) return;
86

9-
if (!accordions.length) return;
10-
11-
accordions.forEach(accordion => {
12-
const button = accordion.querySelector('.faq-question');
13-
const answer = accordion.querySelector('.faq-answer');
7+
console.log("[Initialize] FAQ", faqSection?.id || "all");
148

15-
if (!button || !answer) return;
9+
// Mark as initialized
10+
faqSection.setAttribute('data-faq-initialized', 'true');
1611

17-
button.addEventListener('click', () => {
18-
const isOpen = accordion.getAttribute('data-state') === 'open';
12+
// Find all accordions in this section
13+
const accordions = faqSection.querySelectorAll('.faq-accordion');
14+
if (!accordions.length) return;
15+
16+
accordions.forEach(accordion => {
17+
// Skip if already initialized
18+
if (accordion.hasAttribute('data-initialized')) return;
1919

20-
// Close all other accordions
21-
accordions.forEach(otherAccordion => {
22-
if (otherAccordion !== accordion) {
23-
otherAccordion.setAttribute('data-state', 'closed');
24-
}
25-
});
20+
const button = accordion.querySelector('.faq-question');
21+
const answer = accordion.querySelector('.faq-answer');
2622

27-
// Toggle current accordion
28-
accordion.setAttribute('data-state', isOpen ? 'closed' : 'open');
29-
});
30-
});
31-
}
32-
33-
// Create an observer instance
34-
const observer = new MutationObserver((mutations) => {
35-
for (const mutation of mutations) {
36-
if (mutation.type !== 'childList') continue;
37-
38-
for (const node of mutation.addedNodes) {
39-
// Quick check for element nodes only
40-
if (node.nodeType !== 1) continue;
23+
if (!button || !answer) return;
4124

42-
// Direct class check is faster than matches()
43-
if (node.classList?.contains('faq-section')) {
44-
initializeFAQ(node);
45-
continue;
46-
}
25+
// Mark as initialized to prevent duplicate listeners
26+
accordion.setAttribute('data-initialized', 'true');
4727

48-
// Only query children if needed
49-
const nestedSection = node.getElementsByClassName('faq-section')[0];
50-
if (nestedSection) {
51-
initializeFAQ(nestedSection);
52-
continue;
53-
}
54-
}
55-
}
56-
});
57-
58-
// Start observing with optimized configuration
59-
observer.observe(document.body, {
60-
childList: true,
61-
subtree: true,
62-
attributes: false,
63-
characterData: false
64-
});
65-
66-
// Initialize any existing FAQ sections
67-
document.querySelectorAll('.faq-section').forEach(section => initializeFAQ(section));
68-
69-
// Cleanup when needed
70-
function cleanup() {
71-
observer.disconnect();
28+
button.addEventListener('click', () => {
29+
const isOpen = accordion.getAttribute('data-state') === 'open';
30+
31+
// Close all other accordions in this section
32+
accordions.forEach(otherAccordion => {
33+
if (otherAccordion !== accordion) {
34+
otherAccordion.setAttribute('data-state', 'closed');
35+
}
36+
});
37+
38+
// Toggle current accordion
39+
accordion.setAttribute('data-state', isOpen ? 'closed' : 'open');
40+
});
41+
});
42+
});
7243
}
7344

74-
// Optional: Add cleanup on page unload
75-
window.addEventListener('unload', cleanup);
45+
// Initial run on DOM ready
46+
if (document.readyState === "loading") {
47+
document.addEventListener("DOMContentLoaded", initializeFAQ);
48+
} else {
49+
initializeFAQ();
50+
}

docs/styles/code-groups.js

Lines changed: 11 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
function initializeAllCodeGroups() {
2+
// Find all code group elements that need initialization
3+
document.querySelectorAll('.code-group:not([data-code-group-initialized])').forEach(group => {
4+
initializeCodeGroup(group);
5+
});
6+
}
7+
18
function initializeCodeGroup(group) {
29
if (group.hasAttribute('data-code-group-initialized')) {
310
return;
@@ -113,59 +120,9 @@ function initializeCodeGroup(group) {
113120
group.setAttribute('data-code-group-initialized', 'true');
114121
}
115122

116-
// Setup observer to initialize code groups when they appear
117-
const codeGroupObserver = new MutationObserver((mutations) => {
118-
for (const mutation of mutations) {
119-
if (mutation.type !== 'childList') continue;
120-
121-
for (const node of mutation.addedNodes) {
122-
// Quick check for element nodes only
123-
if (node.nodeType !== 1) continue;
124-
125-
// Direct class check is faster than matches()
126-
if (node.classList?.contains('code-group')) {
127-
initializeCodeGroup(node);
128-
continue;
129-
}
130-
131-
// Only query children if the node might contain code groups
132-
if (node.getElementsByClassName) {
133-
const groups = node.getElementsByClassName('code-group');
134-
for (let i = 0; i < groups.length; i++) {
135-
initializeCodeGroup(groups[i]);
136-
}
137-
}
138-
}
139-
}
140-
});
141-
142-
// Start observing with optimized configuration
143-
codeGroupObserver.observe(document.body, {
144-
childList: true,
145-
subtree: true,
146-
attributes: false,
147-
characterData: false
148-
});
149-
150-
// Cleanup function
151-
function cleanup() {
152-
codeGroupObserver.disconnect();
153-
}
154-
155-
// Add cleanup on page unload
156-
window.addEventListener('unload', cleanup);
157-
158-
// Initialize existing code groups
159-
function initializeExistingCodeGroups() {
160-
const groups = document.getElementsByClassName('code-group');
161-
for (let i = 0; i < groups.length; i++) {
162-
initializeCodeGroup(groups[i]);
163-
}
164-
}
165-
166-
// Initialize if already in DOM
123+
// Initial run on DOM ready
167124
if (document.readyState === "loading") {
168-
document.addEventListener("DOMContentLoaded", initializeExistingCodeGroups);
125+
document.addEventListener("DOMContentLoaded", initializeAllCodeGroups);
169126
} else {
170-
initializeExistingCodeGroups();
171-
}
127+
initializeAllCodeGroups();
128+
}

docs/styles/copy-command.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
1-
window.copyCommand = function(element) {
1+
function initializeAllCopyCommands() {
2+
// Find all copy command containers that need initialization
3+
document.querySelectorAll('.copy-command-container:not([data-copy-initialized])').forEach(container => {
4+
initializeCopyCommand(container);
5+
});
6+
}
7+
8+
function initializeCopyCommand(container) {
9+
// Skip if already initialized
10+
if (container.hasAttribute('data-copy-initialized')) return;
11+
12+
console.log("[Initialize] CopyCommand", container?.id || "unnamed");
13+
14+
// Mark as initialized
15+
container.setAttribute('data-copy-initialized', 'true');
16+
17+
// Attach click event handler to the container
18+
container.addEventListener('click', function() {
19+
copyCommand(this);
20+
});
21+
}
22+
23+
function copyCommand(element) {
224
const container = element.classList.contains("copy-command-container")
325
? element
426
: element.closest(".copy-command-container");
@@ -22,4 +44,14 @@ window.copyCommand = function(element) {
2244
setTimeout(() => {
2345
iconContainer.classList.remove("copied");
2446
}, 1000);
25-
};
47+
}
48+
49+
// Export the function for direct use
50+
window.copyCommand = copyCommand;
51+
52+
// Initial run on DOM ready
53+
if (document.readyState === "loading") {
54+
document.addEventListener("DOMContentLoaded", initializeAllCopyCommands);
55+
} else {
56+
initializeAllCopyCommands();
57+
}

docs/styles/cta.js

Lines changed: 46 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -19,89 +19,56 @@ const CTA_TITLES = [
1919
"Set the stage for serverless success - with ActorCore."
2020
];
2121

22-
function initializeCTA(titleElement) {
23-
console.log("[Initialize] CTA", titleElement?.id || "unnamed");
24-
25-
const subtitle = document.querySelector('.cta-pun-complaint');
26-
27-
if (!titleElement || !subtitle || titleElement.hasAttribute('data-initialized')) return;
28-
29-
titleElement.setAttribute('data-initialized', 'true');
30-
31-
let currentIndex = 0;
32-
let clickCount = 0;
33-
34-
function getNextTitle() {
35-
currentIndex = (currentIndex + 1) % CTA_TITLES.length;
36-
return CTA_TITLES[currentIndex];
37-
}
38-
39-
subtitle.addEventListener('click', () => {
40-
titleElement.textContent = getNextTitle();
41-
clickCount++;
22+
function initializeAllCTAs() {
23+
// Find CTA container
24+
document.querySelectorAll('.cta-container:not([data-cta-initialized])').forEach(container => {
25+
// Skip if already initialized
26+
if (container.hasAttribute('data-cta-initialized')) return;
27+
28+
console.log("[Initialize] CTA", container?.id || "unnamed");
29+
30+
// Mark as initialized
31+
container.setAttribute('data-cta-initialized', 'true');
32+
33+
const titleElement = container.querySelector('#rotating-cta-title');
34+
const subtitle = container.querySelector('.cta-pun-complaint');
4235

43-
if (clickCount === 1) {
44-
subtitle.textContent = "Click here to file another complaint.";
45-
} else if (clickCount === 2) {
46-
subtitle.textContent = "And another.";
47-
} else if (clickCount === 3) {
48-
subtitle.textContent = "Keep clicking.";
49-
} else if (clickCount === 4) {
50-
subtitle.textContent = "I promise this one will stop the puns.";
51-
} else if (clickCount === 5) {
52-
subtitle.textContent = "Fool me once, shame on me. Fool me twice... keep clicking.";
53-
} else if (clickCount === 6) {
54-
subtitle.textContent = "Insanity is doing the same thing over and over again and expecting different results.";
55-
} else if (clickCount >= 7) {
56-
subtitle.textContent = `Your measure of insanity: ${clickCount}`;
36+
if (!titleElement || !subtitle) return;
37+
38+
let currentIndex = 0;
39+
let clickCount = 0;
40+
41+
function getNextTitle() {
42+
currentIndex = (currentIndex + 1) % CTA_TITLES.length;
43+
return CTA_TITLES[currentIndex];
5744
}
58-
});
59-
}
60-
61-
// Setup observer to initialize CTA when it appears
62-
const ctaObserver = new MutationObserver((mutations) => {
63-
for (const mutation of mutations) {
64-
if (mutation.type !== 'childList') continue;
6545

66-
for (const node of mutation.addedNodes) {
67-
// Quick check for element nodes only
68-
if (node.nodeType !== 1) continue;
69-
70-
// Check if this node is the CTA title
71-
if (node.id === 'rotating-cta-title') {
72-
initializeCTA(node);
73-
continue;
74-
}
75-
76-
// Check for CTA title in children
77-
const ctaTitle = node.querySelector('#rotating-cta-title');
78-
if (ctaTitle) {
79-
initializeCTA(ctaTitle);
46+
subtitle.addEventListener('click', () => {
47+
titleElement.textContent = getNextTitle();
48+
clickCount++;
49+
50+
if (clickCount === 1) {
51+
subtitle.textContent = "Click here to file another complaint.";
52+
} else if (clickCount === 2) {
53+
subtitle.textContent = "And another.";
54+
} else if (clickCount === 3) {
55+
subtitle.textContent = "Keep clicking.";
56+
} else if (clickCount === 4) {
57+
subtitle.textContent = "I promise this one will stop the puns.";
58+
} else if (clickCount === 5) {
59+
subtitle.textContent = "Fool me once, shame on me. Fool me twice... keep clicking.";
60+
} else if (clickCount === 6) {
61+
subtitle.textContent = "Insanity is doing the same thing over and over again and expecting different results.";
62+
} else if (clickCount >= 7) {
63+
subtitle.textContent = `Your measure of insanity: ${clickCount}`;
8064
}
81-
}
82-
}
83-
});
84-
85-
// Start observing with optimized configuration
86-
ctaObserver.observe(document.body, {
87-
childList: true,
88-
subtree: true,
89-
attributes: false,
90-
characterData: false
91-
});
92-
93-
// Initialize existing CTA if present
94-
const existingCTA = document.getElementById('rotating-cta-title');
95-
if (existingCTA) {
96-
initializeCTA(existingCTA);
65+
});
66+
});
9767
}
9868

99-
// Initialize if already in DOM
69+
// Initial run on DOM ready
10070
if (document.readyState === "loading") {
101-
document.addEventListener("DOMContentLoaded", () => {
102-
const ctaTitle = document.getElementById('rotating-cta-title');
103-
if (ctaTitle) {
104-
initializeCTA(ctaTitle);
105-
}
106-
});
107-
}
71+
document.addEventListener("DOMContentLoaded", initializeAllCTAs);
72+
} else {
73+
initializeAllCTAs();
74+
}

0 commit comments

Comments
 (0)