Monitoring systems in Jurassic Park must react to changes in real time - a dinosaur appearing in a zone, a change in the viewing area size, mutations in data structures. JavaScript offers advanced APIs for observing these changes without the need for constant polling.
IntersectionObserver informs you when an element enters or leaves the visible area of the page. Ideal for lazy loading, infinite scroll, and scroll-triggered animations.
1// Basic usage
2const observer = new IntersectionObserver((entries) => {
3 entries.forEach(entry => {
4 if (entry.isIntersecting) {
5 console.log(entry.target.id + ' is visible');
6 entry.target.classList.add('visible');
7 } else {
8 entry.target.classList.remove('visible');
9 }
10 });
11}, {
12 threshold: 0.5, // Requires 50% visibility
13 rootMargin: '100px' // Additional margin "before" the element
14});
15
16// Observe elements
17document.querySelectorAll('.zone-card').forEach(card => {
18 observer.observe(card);
19});
20
21// Lazy loading images
22const imageObserver = new IntersectionObserver((entries) => {
23 entries.forEach(entry => {
24 if (entry.isIntersecting) {
25 const img = entry.target;
26 img.src = img.dataset.src; // Load image
27 imageObserver.unobserve(img); // Stop observing
28 }
29 });
30});
31
32document.querySelectorAll('img[data-src]').forEach(img => {
33 imageObserver.observe(img);
34});MutationObserver reacts to changes in the DOM tree - adding/removing elements, attribute changes, text modifications.
1// Observe changes in a container
2const mutObserver = new MutationObserver((mutations) => {
3 mutations.forEach(mutation => {
4 if (mutation.type === 'childList') {
5 mutation.addedNodes.forEach(node => {
6 if (node.nodeType === 1) { // Element
7 console.log('Element added:', node.tagName);
8 }
9 });
10 mutation.removedNodes.forEach(node => {
11 if (node.nodeType === 1) {
12 console.log('Element removed:', node.tagName);
13 }
14 });
15 }
16 if (mutation.type === 'attributes') {
17 console.log('Attribute changed:', mutation.attributeName);
18 }
19 });
20});
21
22const container = document.getElementById('dino-list');
23mutObserver.observe(container, {
24 childList: true, // Observe adding/removing children
25 attributes: true, // Observe attribute changes
26 subtree: true // Also observe descendants
27});
28
29// Stop observing
30// mutObserver.disconnect();ResizeObserver reacts to element size changes - not just the window, but any DOM element.
1const resizeObserver = new ResizeObserver((entries) => {
2 entries.forEach(entry => {
3 const { width, height } = entry.contentRect;
4 console.log(entry.target.id + ':', width + 'x' + height);
5
6 // Adjust layout based on size
7 if (width < 400) {
8 entry.target.classList.add('compact');
9 } else {
10 entry.target.classList.remove('compact');
11 }
12 });
13});
14
15resizeObserver.observe(document.getElementById('dashboard'));localStorage and sessionStorage allow you to store data in the browser.
1// localStorage - data persists after closing the browser
2localStorage.setItem('parkSettings', JSON.stringify({
3 theme: 'dark',
4 alertLevel: 3,
5 lastVisit: Date.now()
6}));
7
8const settings = JSON.parse(localStorage.getItem('parkSettings'));
9console.log(settings.theme); // 'dark'
10
11// sessionStorage - data disappears after closing the tab
12sessionStorage.setItem('currentZone', 'A');
13
14// Listening for changes (between tabs!)
15window.addEventListener('storage', (event) => {
16 console.log('Changed:', event.key);
17 console.log('Old value:', event.oldValue);
18 console.log('New value:', event.newValue);
19});
20
21// Removing
22localStorage.removeItem('parkSettings');
23localStorage.clear(); // Remove everything1// Copying text
2async function copyToClipboard(text) {
3 try {
4 await navigator.clipboard.writeText(text);
5 console.log('Copied:', text);
6 } catch (err) {
7 console.error('Copy error:', err);
8 }
9}
10
11// Pasting text
12async function pasteFromClipboard() {
13 try {
14 const text = await navigator.clipboard.readText();
15 return text;
16 } catch (err) {
17 console.error('No clipboard access');
18 }
19}
20
21// Copy button with feedback
22const copyBtn = document.getElementById('copy-btn');
23copyBtn.addEventListener('click', async () => {
24 const data = document.getElementById('sensor-data').textContent;
25 await copyToClipboard(data);
26 copyBtn.textContent = 'Copied!';
27 setTimeout(() => copyBtn.textContent = 'Copy', 2000);
28});1// Adding a history entry (without page reload)
2history.pushState({ zone: 'A' }, '', '/park/zone-a');
3history.pushState({ zone: 'B' }, '', '/park/zone-b');
4
5// Responding to the "Back" button
6window.addEventListener('popstate', (event) => {
7 if (event.state) {
8 console.log('Returning to zone:', event.state.zone);
9 loadZone(event.state.zone);
10 }
11});
12
13// Replacing the current entry (without adding a new one)
14history.replaceState({ zone: 'A', view: 'map' }, '', '/park/zone-a?view=map');Adding many elements at once is expensive. DocumentFragment allows you to prepare elements "outside the DOM" and add them in a single operation:
1// WITHOUT fragment - each appendChild causes a reflow
2const list = document.getElementById('dino-list');
3const dinosaurs = ['Rex', 'Blue', 'Brachio', 'Stego', 'Trike'];
4
5// BAD - 5 separate DOM operations
6dinosaurs.forEach(name => {
7 const li = document.createElement('li');
8 li.textContent = name;
9 list.appendChild(li); // 5x reflow!
10});
11
12// GOOD - DocumentFragment
13const fragment = document.createDocumentFragment();
14dinosaurs.forEach(name => {
15 const li = document.createElement('li');
16 li.textContent = name;
17 fragment.appendChild(li); // Operation "in memory"
18});
19list.appendChild(fragment); // 1x reflow!"Advanced DOM APIs are the radar and early warning systems of the Park" - Ray Arnold summarizes. "IntersectionObserver is a thermal camera, MutationObserver is an alarm system, and ResizeObserver is an adaptive control screen. Use them wisely!"