, na przykład przez alert(document.getElementById("id").innerHTML), wtedy będzie działać w IE.
Składnia
element.wewnętrznyHTML = „przypisany tekst”
Przykład
Testowanie właściwości innerHTML
Akapit do wstawienia
Obiektowy model dokumentu lub „DOM” to interfejs oprogramowania dostęp do elementów stron internetowych. Zasadniczo jest to interfejs API strony, który umożliwia odczytywanie i manipulowanie zawartością, strukturą i stylami strony. Zobaczmy, jak to działa i jak działa.
Jak zbudowana jest strona internetowa?
Proces przekształcania źródłowego dokumentu HTML w nadającą się do renderowania, stylizowaną i interaktywną stronę nazywa się „krytyczną ścieżką renderowania”. Chociaż proces ten można podzielić na kilka kroków, jak opisałem w Zrozumieniu krytycznej ścieżki renderowania, kroki te można z grubsza podzielić na dwa kroki. W pierwszym przeglądarka analizuje dokument, aby określić, co ostatecznie zostanie wyświetlone na stronie, a w drugim przeglądarka wykonuje renderowanie.
Rezultatem pierwszego etapu jest tzw. „drzewo renderowania”. renderowanie drzewa- to jest przedstawienie elementy HTML, które będą wyświetlane na stronie, oraz powiązane z nimi style. Aby zbudować to drzewo, przeglądarka potrzebuje dwóch rzeczy:
- CSSOM, reprezentujący style powiązane z elementami
- DOM, reprezentacja elementów
Z czego zrobiony jest DOM?
DOM to obiektowa reprezentacja oryginalnego dokumentu HTML. Ma pewne różnice, jak zobaczymy poniżej, ale w istocie jest to próba przekształcenia struktury i zawartości dokumentu HTML w model obiektowy, z którego mogą korzystać różne programy.
Struktura obiektów DOM jest reprezentowana przez tzw. „drzewo węzłów”. Jest tak nazwany, ponieważ można go traktować jako drzewo z jednym rodzicem, które rozgałęzia się na wiele gałęzi podrzędnych, z których każda może mieć liście. W tym przypadku „element” nadrzędny jest elementem głównym, „gałęzie” potomne to elementy zagnieżdżone, a „liście” to zawartość elementów.
Weźmy ten dokument HTML jako przykład:
mój pierwszy Strona internetowa
Witaj świecie!
Jak się masz?
Ten dokument może być reprezentowany jako następujące drzewo węzłów:
- html
- głowa
- tytuł
- Moja pierwsza strona internetowa
- ciało
Czym DOM nie jest?
W powyższym przykładzie wygląda na to, że DOM jest odwzorowaniem 1:1 oryginalnego dokumentu HTML. Jednak, jak powiedziałem, są różnice. Aby w pełni zrozumieć, czym jest DOM, musimy przyjrzeć się, czym nie jest.
DOM nie jest kopią oryginalnego kodu HTML
Chociaż DOM jest tworzony z dokumentu HTML, nie zawsze jest dokładnie taki sam. Istnieją dwa przypadki, w których DOM może różnić się od oryginalnego kodu HTML.
1. Gdy HTML zawiera błędy znaczników
DOM to interfejs umożliwiający dostęp do rzeczywistych (tj. już wyrenderowanych) elementów dokumentu HTML. W procesie tworzenia DOM sama przeglądarka może naprawić niektóre błędy w kodzie HTML.
Rozważ ten dokument HTML jako przykład:
Witaj świecie!
Brakujące elementy w dokumencie
oraz , który jest wymagany dla HTML. Ale jeśli spojrzymy na powstałe drzewo DOM, widzimy, że zostało to naprawione:
- html
2. Kiedy DOM jest modyfikowany przez kod JavaScript
Oprócz tego, że jest interfejsem do przeglądania zawartości dokumentu HTML, sam DOM może być również modyfikowany.
Możemy na przykład stworzyć dodatkowe węzły dla DOM za pomocą Javascript.
Var nowyParagraf = document.createElement("p"); var paragraphContent = document.createTextNode("Jestem nowy!"); newParagraph.appendChild(paragraphContent); document.body.appendChild(newParagraph);
Ten kod zmieni DOM, ale zmiany nie pojawią się w Dokument HTML.
DOM nie jest tym, co widzisz w przeglądarce (tj. drzewo renderowania)
W oknie przeglądarki widać drzewo renderowania, które, jak powiedziałem, jest kombinacją DOM i CSSOM. Tym, co odróżnia DOM od drzewa renderowania, jest to, że składa się ono tylko z tego, co ostatecznie zostanie wyrenderowane na ekranie.
Ponieważ drzewo renderowania dotyczy tylko tego, co jest wyświetlane, wyklucza elementy, które są wizualnie ukryte. Na przykład elementy, które mają style z Nie wyświetla się.
Witaj świecie!
Jak się masz?
DOM będzie zawierał element
Jednak drzewo renderowania, a tym samym to, co jest widoczne w rzutni, nie zostanie uwzględnione w tym elemencie.
DOM nie jest tym, co jest wyświetlane w DevTools
Ta różnica jest nieco mniejsza, ponieważ DevTools Element Inspector zapewnia najbliższe przybliżenie DOM, który mamy w przeglądarce. Jednak inspektor DevTools zawiera: Dodatkowe informacje, którego nie ma w DOM.
Najlepszym tego przykładem są pseudoelementy CSS. Pseudoelementy tworzone za pomocą selektorów ::zanim oraz ::po, są częścią CSSOM i drzewa renderowania, ale technicznie nie są częścią modelu DOM. Dzieje się tak dlatego, że DOM jest generowany tylko z oryginalnego dokumentu HTML, bez uwzględnienia stylów zastosowanych do elementu.
Mimo że pseudoelementy nie są częścią DOM, znajdują się w naszym inspektorze elementów devtools.
Streszczenie
DOM to interfejs do dokumentu HTML. Jest używany przez przeglądarki jako pierwszy krok w określaniu, co należy renderować w oknie roboczym, oraz przez kod JavaScript do zmiany zawartości, struktury lub stylu strony.
Odniesienie zawiera opis wszystkich właściwości i metod standardowych wbudowanych obiektów JavaScript.
Obiektowy model dokumentu
Obiektowy model dokumentu(Document Object Model, DOM) to interfejs programowania aplikacji (API) dla XML, który został rozszerzony o pracę z HTML.
W DOM cała zawartość strony (elementy i tekst) jest reprezentowana jako hierarchia węzłów. Rozważ następujący kod:
prosta strona
Witaj świecie!
Ten kod można przedstawić za pomocą DOM jako hierarchii węzłów:
Reprezentując dokument jako drzewo węzłów, DOM API daje programistom pełną kontrolę nad zawartością i strukturą strony internetowej.
Opisując strukturę drzewa DOM, używa się terminologii zapożyczonej z drzew genealogicznych.
Zatem węzeł znajdujący się bezpośrednio nad danym węzłem nazywany jest węzłem nadrzędnym danego węzła. Węzły znajdujące się jeden poziom poniżej tego węzła nazywane są dziećmi tego węzła. Węzły znajdujące się na tym samym poziomie i mające tego samego rodzica nazywane są rodzeństwem lub rodzeństwem. Węzły znajdujące się o dowolną liczbę poziomów poniżej danego węzła nazywane są jego potomkami. Rodzice, dziadkowie i wszelkie inne węzły znajdujące się na dowolnej liczbie poziomów nad danym węzłem nazywani są jego przodkami.
Odwołanie DOM zawiera opis obiektów Document, Element, Event i NodeList, w tym opis ich metod i właściwości:
Katalog BOM
BOM (Browser Object Model w tłumaczeniu z języka angielskiego - Browser Object Model) zapewnia dostęp do okna przeglądarki i pozwala manipulować nim i jego elementami.
Obiekty BOM zapewniają dostęp do funkcjonalności przeglądarki niezależnie od zawartości strony internetowej. Temat BOM jest jednocześnie ciekawy i złożony, ponieważ ze względu na długi brak specyfikacji producenci przeglądarek swobodnie rozszerzali BOM według własnego uznania. Wiele elementów podobnych do różne przeglądarki, stały się de facto standardami przestrzeganymi do dziś ze względu na wzajemną kompatybilność. Aby ujednolicić te podstawowe aspekty JavaScript, W3C zdefiniowało podstawowe elementy BOM w specyfikacji HTML5.
Zazwyczaj programiści używają jQuery, gdy muszą coś zrobić z DOM. Jednak prawie każdą manipulację DOM można wykonać w czystym JavaScript przy użyciu jego API DOM.
Przyjrzyjmy się temu API bardziej szczegółowo:
Na koniec napiszesz własną prostą bibliotekę DOM, którą będzie można wykorzystać w dowolnym projekcie.
Zapytania DOM
Zapytania DOM są wykonywane przy użyciu metody .querySelector(), która jako argument przyjmuje dowolny selektor CSS.
Const myElement = document.querySelector("#foo > div.bar")
Zwróci pierwszy pasujący element. Możesz zrobić odwrotnie - sprawdź, czy element pasuje do selektora:
MójElement.matches("div.bar") === true
Jeśli chcesz pobrać wszystkie elementy pasujące do selektora, użyj następującej konstrukcji:
Const myElements = document.querySelectorAll(.bar")
Jeśli wiesz, do którego elementu nadrzędnego się odnosić, możesz po prostu przeszukać jego elementy potomne, zamiast przeszukiwać cały kod:
Const myChildElemet = myElement.querySelector("input") // Zamiast: // document.querySelector("#foo > div.bar input")
Powstaje pytanie: po co więc używać innych, mniej wygodnych metod, takich jak.getElementsByTagName() ? Jest mały problem - wynik wyjścia .querySelector() nie jest aktualizowany, a gdy dodamy nowy element (patrz ), nie ulegnie on zmianie.
const elements1 = document.querySelectorAll("div") const elements2 = document.getElementsByTagName("div") const newElement = document.createElement("div") document.body.appendChild(newElement) elements1.length ===elementy2.length // fałszywy
Również querySelectorAll() zbiera wszystko w jedną listę, co czyni ją niezbyt wydajną.
Jak pracować z listami?
Co więcej, .querySelectorAll() ma dwa małe dziwactwa. Nie możesz po prostu wywoływać metod na wynikach i oczekiwać, że zostaną zastosowane do każdego z nich (jak możesz być przyzwyczajony do robienia z jQuery). W każdym razie będziesz musiał wykonać iterację po wszystkich elementach pętli. Po drugie, zwracany obiekt jest listą elementów, a nie tablicą. Dlatego metody tablicowe nie będą działać. Oczywiście istnieją metody dla list, takie jak .forEach() , ale niestety nie są one odpowiednie dla wszystkich przypadków. Lepiej więc przekonwertować listę na tablicę:
// Używanie Array.from() Array.from(myElements).forEach(doSomethingWithEachElement) // Lub prototyp tablicy (przed ES6) Array.prototype.forEach.call(myElements, doSomethingWithEachElement) // Łatwiej: .forEach.call (mojeElementy , zróbCośZKażdymElementem)
Każdy element ma pewne właściwości, które odnoszą się do „rodziny”.
MójElement.dzieci mójElement.firstElementMoje dzieckoElement.lastElementMojElement.dziecko.previousElementRodzeństwo mójElement.nextElementRodzeństwo
Ponieważ interfejs Element jest dziedziczony z interfejsu węzła, obecne są również następujące właściwości:
MyElement.childNodes myElement.firstChild myElement.lastChild myElement.previousSibling myElement.nextSibling myElement.parentNode myElement.parentElement
Pierwsze właściwości odnoszą się do elementu, a drugie (z wyjątkiem .parentElement) mogą być listami elementów dowolnego typu. W związku z tym możesz sprawdzić typ elementu:
MyElement.firstChild.nodeType === 3 // ten element będzie węzłem tekstowym
Dodawanie klas i atrybutów
Dodać nowa klasa bardzo prosta:
myElement.classList.add("foo") myElement.classList.remove("bar") myElement.classList.toggle("baz")
Dodanie właściwości do elementu jest dokładnie takie samo, jak dodanie właściwości do dowolnego obiektu:
// Pobierz wartość atrybutu const value = myElement.value // Ustaw atrybut jako właściwość elementu myElement.value = "(!LANG:foo"
// Для установки нескольких свойств используйте.Object.assign()
Object.assign(myElement, {
value: "foo",
id: "bar"
})
// Удаление атрибута
myElement.value = null
!}
Możesz użyć metod .getAttibute() , .setAttribute() i .removeAttribute() . Natychmiast zmienią atrybuty HTML elementu (w przeciwieństwie do właściwości DOM), co uruchomi renderowanie przeglądarki (możesz zobaczyć wszelkie zmiany, sprawdzając element za pomocą narzędzi programistycznych w przeglądarce). Takie przerysowania nie tylko wymagają więcej zasobów niż ustawianie właściwości DOM, ale mogą również prowadzić do nieoczekiwanych błędów.
Są one zwykle używane w elementach, które nie mają odpowiednich właściwości DOM, takich jak colspan . Lub jeśli ich użycie jest naprawdę konieczne, na przykład dla właściwości HTML podczas dziedziczenia (patrz).
Dodawanie stylów CSS
Dodaje się je w taki sam sposób, jak inne właściwości:
MyElement.style.marginLeft = "2em"
Niektóre specyficzne właściwości można ustawić za pomocą .style , ale jeśli chcesz uzyskać wartości po pewnych obliczeniach, lepiej użyć window.getComputedStyle() . Ta metoda pobiera element i zwraca CSSStyleDeclaration zawierającą style zarówno samego elementu, jak i jego rodzica:
Window.getComputedStyle(myElement).getPropertyValue("pozostały margines")
Zmiana DOM
Możesz przenosić elementy:
// Dodanie elementu 1 jako ostatniego dziecka elementu 2 element1.appendChild(element2) // Wstawienie elementu 2 jako dziecka elementu 1 przed elementem 3 element1.insertBefore(element2, element3)
Jeśli nie chcesz się przenosić, ale chcesz wkleić kopię, użyj:
// Utwórz klon const myElementClone = myElement.cloneNode() myParentElement.appendChild(myElementClone)
Metoda .cloneNode() przyjmuje jako argument wartość logiczną, a jeśli true, elementy potomne są również klonowane.
Oczywiście możesz tworzyć nowe elementy:
const myNewElement = document.createElement("div") const myNewTextNode = document.createTextNode("jakiś tekst")
A następnie wstaw je, jak pokazano powyżej. Nie możesz usunąć elementu bezpośrednio, ale możesz to zrobić poprzez element nadrzędny:
MyParentElement.removeChild(myElement)
Możesz również odwołać się pośrednio:
MyElement.parentNode.removeChild(myElement)
Metody na elementach
Każdy element ma właściwości takie jak .innerHTML i .textContent , zawierają kod HTML i odpowiednio sam tekst. Poniższy przykład zmienia zawartość elementu:
// Zmień kod HTML myElement.innerHTML = `
Nowa treść
bip bup bip bup
` // Spowoduje to usunięcie treści myElement.innerHTML = null // Dodaje do HTML myElement.innerHTML += ` kontynuuj czytanie...
W rzeczywistości zmiana kodu HTML jest złym pomysłem, ponieważ powoduje utratę wszystkich zmian, które zostały wprowadzone wcześniej, a także przeciąża obsługę zdarzeń. Lepiej jest używać tej metody tylko poprzez całkowite odrzucenie całego kodu HTML i zastąpienie go kopią z serwera. Lubię to:
const link = document.createElement("a") const text = document.createTextNode("kontynuuj czytanie...") const hr = document.createElement("hr") link.href = "foo.html" link.appendChild( tekst) myElement.appendChild(link) myElement.appendChild(hr)
Spowoduje to jednak dwa przerysowania przeglądarki, podczas gdy .innerHTML spowoduje tylko jedno. Możesz obejść ten problem, jeśli najpierw dodasz wszystko do DocumentFragment , a następnie dodasz potrzebny fragment:
Const fragment = document.createDocumentFragment() fragment.appendChild(tekst) fragment.appendChild(hr) myElement.appendChild(fragment)
Obsługa zdarzeń
Jeden z najprostszych handlerów:
MyElement.onclick = funkcja onclick (zdarzenie) ( console.log(event.type + " został zwolniony") )
Ale co do zasady należy tego unikać. Tutaj .onclick jest właściwością elementu i teoretycznie możesz ją zmienić, ale nie będziesz w stanie dodać innych funkcji obsługi za pomocą kolejnej funkcji, która odwołuje się do starej.
Lepiej jest użyć .addEventListener(), aby dodać moduły obsługi. Przyjmuje trzy argumenty: typ zdarzenia, funkcję, która będzie wywoływana przy każdym uruchomieniu, oraz obiekt konfiguracyjny (do tego dojdziemy później).
MyElement.addEventListener("klik", funkcja (zdarzenie) ( console.log(zdarzenie.type + " zostało uruchomione") )) myElement.addEventListener("klik", funkcja (zdarzenie) ( console.log(event.type + " został zwolniony") ))
Właściwość event.target odwołuje się do elementu, do którego jest dołączone zdarzenie.
Dzięki temu możesz uzyskać dostęp do wszystkich właściwości:
// Właściwość `forms` jest tablicą zawierającą łącza do wszystkich formularzy const myForm = document.forms const myInputElements = myForm.querySelectorAll("input") Array.from(myInputElements).forEach(el => ( el.addEventListener(") zmiana", funkcja (zdarzenie) ( console.log(event.target.value) )) ))
Zapobieganie domyślnym działaniom
W tym celu używana jest metoda .preventDefault(), która blokuje standardowe akcje. Na przykład zablokuje przesłanie formularza, jeśli autoryzacja po stronie klienta nie powiodła się:
MyForm.addEventListener("submit", function (event) ( const name = this.querySelector("#name") if (name.value === "(!LANG:Donald Duck") {
alert("You gotta be kidding!")
event.preventDefault()
}
})
!}
Metoda .stopPropagation() pomoże, jeśli masz określoną procedurę obsługi zdarzeń dołączoną do elementu podrzędnego i drugą procedurę obsługi tego samego zdarzenia dołączoną do elementu nadrzędnego.
Jak wspomniano wcześniej, metoda .addEventListener() przyjmuje opcjonalny trzeci argument jako obiekt konfiguracyjny. Ten obiekt musi zawierać jedną z następujących właściwości logicznych (wszystkie domyślnie ustawione na false):
- przechwytywanie: zdarzenie zostanie dołączone do tego elementu przed jakimkolwiek innym elementem poniżej w DOM;
- raz: wydarzenie można przypiąć tylko raz.
- pasywny: event.preventDefault() zostanie zignorowany (wyjątek podczas błędu).
Najpopularniejszą właściwością jest .capture i jest tak powszechna, że istnieje krótka droga wpisy: zamiast przekazywać go w obiekcie konfiguracyjnym, po prostu podaj jego wartość tutaj:
MyElement.addEventListener(typ, listener, true)
Programy obsługi są usuwane za pomocą metody .removeEventListener(), która przyjmuje dwa argumenty: typ zdarzenia i odwołanie do procedury obsługi do usunięcia. Na przykład właściwość Once można zaimplementować w następujący sposób:
MyElement.addEventListener("zmiana", funkcja listener (zdarzenie) ( console.log(event.type + " został wyzwolony na " + this) this.removeEventListener("change", listener) ))
Dziedzictwo
Załóżmy, że masz element i chcesz dodać obsługę zdarzeń dla wszystkich jego elementów podrzędnych. Następnie musiałbyś przejść przez nie za pomocą metody myForm.querySelectorAll("input"), jak pokazano powyżej. Możesz jednak po prostu dodać elementy do formularza i sprawdzić ich zawartość za pomocą event.target .
MyForm.addEventListener("change", function (event) ( const target = event.target if (target.matches("input")) ( console.log(target.value) ) ))
I kolejny plus Ta metoda jest to, że handler zostanie automatycznie powiązany z nowymi elementami podrzędnymi.
Animacja
Najłatwiejszym sposobem dodania animacji jest użycie CSS z właściwością przejścia. Ale dla większej elastyczności (na przykład w przypadku gry) lepiej nadaje się JavaScript.
Wywołanie metody window.setTimeout() do momentu zakończenia animacji nie jest najlepszy pomysł, ponieważ aplikacja może się zawiesić, szczególnie na urządzenia mobilne. Lepiej jest użyć window.requestAnimationFrame(), aby zapisać wszystkie zmiany do następnego przerysowania. Jako argument przyjmuje funkcję, która z kolei otrzymuje znacznik czasu:
const start = window.performance.now() const duration = 2000 window.requestAnimationFrame(funkcja fadeIn (now)) ( const progress = now - start myElement.style.opacity = postęp / czas trwania if (progress< duration) {
window.requestAnimationFrame(fadeIn)
}
}
W ten sposób uzyskuje się bardzo płynną animację. W swoim artykule Mark Brown omawia ten temat.
Pisanie własnej biblioteki
Fakt, że w DOM musisz cały czas iterować elementy, aby coś z nimi zrobić, może wydawać się dość żmudny w porównaniu ze składnią $(".foo").css((color: "red")) jQuery. Ale dlaczego nie napisać kilku własnych metod, które ułatwią to zadanie?
Const $ = funkcja $(selector, context = document) ( const elements = Array.from(context.querySelectorAll(selector)) return ( elementy, html (newHtml) ( this.elements.forEach(element => ( element.innerHTML = newHtml )) zwróć to ), css (newCss) ( this.elements.forEach(element => ( Object.assign(element.style, newCss) )) zwróć to ), on (zdarzenie, handler, opcje) ( this.elements .forEach(element => ( element.addEventListener(zdarzenie, handler, opcje) )) zwróć to ) ) )