Systemy monitoringu w Parku Jurajskim muszą reagować na zmiany w czasie rzeczywistym - pojawienie się dinozaura w strefie, zmiana rozmiaru obszaru widoczności, mutacje w strukturze danych. JavaScript oferuje zaawansowane API do obserwowania tych zmian bez konieczności ciągłego odpytywania (polling).
IntersectionObserver informuje, kiedy element wchodzi lub wychodzi z widocznego obszaru strony. Idealny do lazy loading, infinite scroll i animacji przy scrollowaniu.
1// Podstawowe użycie
2const observer = new IntersectionObserver((entries) => {
3 entries.forEach(entry => {
4 if (entry.isIntersecting) {
5 console.log(entry.target.id + ' jest widoczny');
6 entry.target.classList.add('visible');
7 } else {
8 entry.target.classList.remove('visible');
9 }
10 });
11}, {
12 threshold: 0.5, // Wymagane 50% widoczności
13 rootMargin: '100px' // Dodatkowy margines "przed" elementem
14});
15
16// Obserwuj elementy
17document.querySelectorAll('.zone-card').forEach(card => {
18 observer.observe(card);
19});
20
21// Lazy loading obrazów
22const imageObserver = new IntersectionObserver((entries) => {
23 entries.forEach(entry => {
24 if (entry.isIntersecting) {
25 const img = entry.target;
26 img.src = img.dataset.src; // Ładuj obraz
27 imageObserver.unobserve(img); // Przestań obserwować
28 }
29 });
30});
31
32document.querySelectorAll('img[data-src]').forEach(img => {
33 imageObserver.observe(img);
34});MutationObserver reaguje na zmiany w drzewie DOM - dodanie/usunięcie elementów, zmiana atrybutów, modyfikacja tekstu.
1// Obserwuj zmiany w kontenerze
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('Dodano element:', node.tagName);
8 }
9 });
10 mutation.removedNodes.forEach(node => {
11 if (node.nodeType === 1) {
12 console.log('Usunięto element:', node.tagName);
13 }
14 });
15 }
16 if (mutation.type === 'attributes') {
17 console.log('Zmieniony atrybut:', mutation.attributeName);
18 }
19 });
20});
21
22const container = document.getElementById('dino-list');
23mutObserver.observe(container, {
24 childList: true, // Obserwuj dodawanie/usuwanie dzieci
25 attributes: true, // Obserwuj zmiany atrybutów
26 subtree: true // Obserwuj też potomków
27});
28
29// Zatrzymanie obserwowania
30// mutObserver.disconnect();ResizeObserver reaguje na zmianę rozmiaru elementów - nie tylko okna, ale dowolnego elementu DOM.
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 // Dostosuj układ na podstawie rozmiaru
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 i sessionStorage pozwalają przechowywać dane w przeglądarce.
1// localStorage - dane przetrwają zamknięcie przeglądarki
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 - dane znikają po zamknięciu karty
12sessionStorage.setItem('currentZone', 'A');
13
14// Nasłuchiwanie zmian (między kartami!)
15window.addEventListener('storage', (event) => {
16 console.log('Zmieniono:', event.key);
17 console.log('Stara wartość:', event.oldValue);
18 console.log('Nowa wartość:', event.newValue);
19});
20
21// Usuwanie
22localStorage.removeItem('parkSettings');
23localStorage.clear(); // Usuń wszystko1// Kopiowanie tekstu
2async function copyToClipboard(text) {
3 try {
4 await navigator.clipboard.writeText(text);
5 console.log('Skopiowano:', text);
6 } catch (err) {
7 console.error('Błąd kopiowania:', err);
8 }
9}
10
11// Wklejanie tekstu
12async function pasteFromClipboard() {
13 try {
14 const text = await navigator.clipboard.readText();
15 return text;
16 } catch (err) {
17 console.error('Brak dostępu do schowka');
18 }
19}
20
21// Przycisk kopiowania z feedbackiem
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 = 'Skopiowano!';
27 setTimeout(() => copyBtn.textContent = 'Kopiuj', 2000);
28});1// Dodawanie wpisu do historii (bez przeładowania strony)
2history.pushState({ zone: 'A' }, '', '/park/zone-a');
3history.pushState({ zone: 'B' }, '', '/park/zone-b');
4
5// Reagowanie na przycisk "Wstecz"
6window.addEventListener('popstate', (event) => {
7 if (event.state) {
8 console.log('Powrót do strefy:', event.state.zone);
9 loadZone(event.state.zone);
10 }
11});
12
13// Zastępowanie aktualnego wpisu (bez dodawania nowego)
14history.replaceState({ zone: 'A', view: 'map' }, '', '/park/zone-a?view=map');Dodawanie wielu elementów naraz jest kosztowne. DocumentFragment pozwala przygotować elementy "poza DOM" i dodać je jedną operacją:
1// BEZ fragmentu - każde appendChild powoduje reflow
2const list = document.getElementById('dino-list');
3const dinosaurs = ['Rex', 'Blue', 'Brachio', 'Stego', 'Trike'];
4
5// ZLE - 5 oddzielnych operacji DOM
6dinosaurs.forEach(name => {
7 const li = document.createElement('li');
8 li.textContent = name;
9 list.appendChild(li); // 5x reflow!
10});
11
12// DOBRZE - DocumentFragment
13const fragment = document.createDocumentFragment();
14dinosaurs.forEach(name => {
15 const li = document.createElement('li');
16 li.textContent = name;
17 fragment.appendChild(li); // Operacja "w pamięci"
18});
19list.appendChild(fragment); // 1x reflow!"Zaawansowane DOM API to radar i systemy wczesnego ostrzegania Parku" - podsumowuje Ray Arnold. "IntersectionObserver to kamera termowizyjna, MutationObserver to system alarmowy, a ResizeObserver to adaptacyjny ekran kontrolny. Używaj ich mądrze!"