Właściwości elementów

Jeżeli pobierzesz jakikolwiek element ze strony, będziesz mógł w konsoli wyświetlić jego właściwości i metody.
Najlepiej od razu odpal debuggera (F12) i zostaw konsole widoczną, bo jeszcze nam się przyda.

Pobrałem za ciebie powyższy przycisk. Otwórz debugger i rozwiń wypisany element.


const btn = document.querySelector(".button");
console.dir( btn );

Obiekt taki ma bardzo dużo właściwości i metod. Część z nich przechowuje pozycję na stronie, typ elementu, jego rozmiary, inne są wykorzystywane dla zdarzeń itp.
Nie wszystkie będziesz non stop używał. Jeżeli trafi się taka konieczność, wiedz po prostu gdzie szukać tego co cię interesuje (wypisując pobrany element w konsoli za pomocą console.dir i badając odpowiednie właściwości).

Poniżej skupimy się na właściwościach i metodach, które są wykorzystywane najczęściej.

innerHTML i outerHTML

Właściwość innerHTML umożliwia odczyt i ustawianie html, jaki jest we wnętrzu danego elementu:


<button class="button">
    <span>Kliknij mnie</span>
</button>

const btn = document.querySelector(".button");
console.log( btn.innerHTML ); //<span>Kliknij mnie</span>

btn.innerHTML = "<span>Nie klikaj mnie!</span>"

Za pomocą innerHTML możemy oczywiście wstawiać całe kawałki dokumentu:


<div class="test-cnt">Tutaj zaraz coś wstawimy</div>
<button class="button" type="button">Wstaw element</button>

const btn = document.querySelector(".button");

btn.addEventListener("click", () => {
    document.querySelector(".test-cnt").innerHTML = `
        <div class="module">
            <h2>Przykładowy tytuł</h2>
            <p>To jest <strong>dynamicznie wstawiony html</strong></p>
            <button class="button">klik</button>
        </div>
    `;
});
Tutaj zaraz coś wstawimy

Podobną do innerHTML jest właściwość outerHTML, z tym, że zwraca także kod html samego elementu:


const btn = document.querySelector(".btn");
console.log(btn.outerHTML); //<button class="btn" type="button"><span>Kliknij mnie</span></button>
Wstawianie na stronę nowego HTML za pomocą powyższych metod jest z pewnością bardzo wygodne. Trzeba mieć jednak na uwadze, że nie zawsze będzie to najbardziej bezpieczne - szczególnie gdy wstawiamy dane, które pobieramy bezpośrednio z serwera lub formularza wypełnianego przez użytkownika. W obydwu sytuacjach gdy odpowiednio nie zabezpieczymy naszego skryptu, narazimy użytkownika na atak (XSS), który potem odbije się na nas samych (jako właścicieli danej strony). Przy bardziej realnych projektach, w których wykonujemy wiele takich operacji możemy sięgnąć po odpowiednie zabezpieczenia. Jednym z nich jest biblioteka DOMPurify, która służy do odpowiedniego przerabiania wstawianego kodu tak, by był odporny na ataki XSS. Innym rozwiązaniem jest sięgnięcie po Sanitizer Api.

innerText i textContent

Właściwości innerText i textContent działają podobnie do powyższej innerHTML, z tym, że zwracają lub ustawiają sam tekst (bez znaczników HTML):


const btn = document.querySelector(".btn");

console.log(btn.innerHTML); //<span>Kliknij mnie</span>
console.log(btn.innerText); ///"Kliknij mnie"
console.log(btn.textContent); //"Kliknij mnie"

Obie właściwości pozwalają nam zwrócić i ustawić tekst elementu. Rozróżnia je to, że innerText działa na sparsowanym tekście, natomiast textContent na oryginalnym, jaki wpisaliśmy do pliku HTML.

Co to oznacza w praktyce? Gdy pobierasz tekst za pomocą innerText, zostanie on pobrany z zaaplikowanymi stylami. Fragmenty tekstu, które zostały ukryte za pomocą display:none i visibility:hidden nie zostaną zwrócone. Dodatkowo tekst taki nie będzie miał wcięć, a wszystkie napotkane znaczniki <br> i nowe linie w HTML zostaną zwrócone jako przejście do nowej linii.

Pobierając ten sam tekst za pomocą textContent zwróci nam tekst oryginalny, jaki napisaliśmy w pliku HTML z wszystkimi wcięciami i enterami które zrobiliśmy za pomocą tabulatora.


<div>
    <span style="display: none">Ukryty</span>
    <span>Obok mnie są dwa niewidoczne spany</span>
    <span style="visibility: hidden">Niewidoczny</span>
</div>

<div>
    Turlał goryl po górach kolorowe korale
    Góral tarł na tarze kartofle wytrwale
</div>
Ukryty Obok mnie są dwa niewidoczne spany Niewidoczny
Turlał goryl po górach kolorowe korale Góral tarł na tarze kartofle wytrwale

To samo tyczy się wstawiania tekstu do elementu. Ustawiając wieloliniowy tekst za pomocą textContent zostanie on po prostu wstawiony do elementu w oryginalnej postaci, natomiast ten sam tekst wstawiany za pomocą innerText automatycznie doda nam <br>


const text = `
    Mała muszka spod Łopuszki
    chciała mieć różowe nóżki
    Różdżką nóżki czarowała
    lecz wciąż nóżki czarne miała
`;

const div = document.querySelector("#addTextTest");

const btn1 = document.querySelector("#textContent");
btn1.addEventListener("click", e => {
    div.textContent = text.trim();
});

const btn2 = document.querySelector("#innerText");
btn2.addEventListener("click", e => {
    div.innerText = text.trim();
});

tagName

Właściwość tagName zwraca nam nazwę elementu np. h2, a, p itp.


const btn = document.querySelector(".button");
console.log(btn.tagName);

Do czego to może się przydać? Przykładowo można pobrać wiele elementów na raz, a następnie tylko dla określonych coś zmieniać:


const elements = document.querySelectorAll("body *");

for (const el of elements) {
    if (el.tagName === "STRONG") {
        el.style.border = "1px solid red";
    }
}

Dla elementów pobranych ze strony HTML nazwy znaczników są zwracane dużymi literami. Dla strony - np. xhtml już nie zawsze będzie to regułą.

Praca z atrybutami

Do pracy z atrybutami danego elementu (np. src dla img) możemy skorzystać z poniższych metod:

el.getAttribute(name) pobiera wartość danego atrybutu lub zwraca null jeżeli takiego nie ma
el.setAttribute(name, value) ustawia nową wartość atrybutu
el.hasAttribute(name) zwraca true/false w zależności czy element ma atrybut o danej nazwie
el.removeAttribute(name) usuwa atrybut o danej nazwie
el.toggleAttribute(name) dodaje/usuwa dany atrybut

<a href="http://google.pl"> Wyszukaj </a>

const link = document.querySelector("a");

const href = link.getAttribute("href"); //"http://google.pl"
const target = link.getAttribute("target"); //null

link.setAttribute("target", "_blank");
if (link.hasAttribute("target")) {
    console.log(link.getAttribute("target")); //"_blank"
}

const btn = document.querySelector("button");
const input = document.querySelector("input");

btn.addEventListener("click", e => {
    input.toggleAttribute("readonly");

    if (input.hasAttribute("readonly")) {
        btn.innerText = "zakończ";
    } else {
        btn.innerText = "edytuj";
    }
})

dataset

Atrybuty możemy podzielić na standardowe dostępne dla danych znaczników (np. src, alt, title, class itp.), oraz nasze własne. Te drugie powinny zaczynać się od słowa data- np. data-text, data-direction.

Własne atrybuty możemy obsługiwać za pomocą powyższych metod, ale możemy dla nich skorzystać z właściwości dataset. Jest to obiekt, którego kolejne właściwości są budowane na bazie niestandardowych atrybutów:


<div class="module"
    data-type="important"
    data-position="top"
    data-my-custom-data="Przykładowy tekst"
> ... </div>

const tooltip = document.querySelector(".module");

console.log(tooltip.dataset.type); //"important"
console.log(tooltip.dataset.position); //"top"
console.log(tooltip.dataset.myCustomData); //"Przykładowy tekst"

Przy podawaniu nazwy danego atrybutu pomijamy początek data-, a myślniki w nazwie zamieniamy na zapis camelCase.


tooltip.dataset.myCustomData = "nowa wartość"; //utworzy w elemencie atrybut data-my-custom-data="nowa wartość"
tooltip.dataset.style = "primary"; //utworzy atrybut data-style="primary"
tooltip.dataset.moduleSize = "big" //utworzy atrybut data-module-size="big"

//to samo możemy uzyskać za pomocą getAttribute i setAttribute
tooltip.setAttribute("data-custom-data", "nowa wartość");
tooltip.setAttribute("data-style", "primary");
tooltip.setAttribute("data-module-size", "big");

Charakterystyczną rzeczą jest to, że atrybuty w znacznikach html zawsze są tekstem. Tak więc cokolwiek byśmy nie przetrzymywali w dataset, staje się to tekstem:


<div data-nr1="16" data-nr2="30"> ... </div>

console.log(typeof div.dataset.nr1, typeof div.dataset.nr2) //"string", "string"
console.log(div.dataset.nr1 + div.dataset.nr2); //"1630"

Niestandardowe atrybuty idealnie nadają się więc do przechowywania tekstów dynamicznie wstawianych na stronę, kawałków html, które zaraz wmontujemy w stronę, czy dodatkowych wartości służących do stylowania za pomocą atrybutów.


div.dataset.type = "error";

div[data-type="error"] {
    color: red;
}

Przykładem (jednym z setek) mogą być tooltipy w popularnym frameworku Bootstrap. Dzięki dodatkowych atrybutom określana jest tutaj pozycja dymka.

bootstrap tooltipy

Przy liczbach także nie powinno być większych problemów, ponieważ łatwo możemy je konwertować:


console.log( +div.dataset.nr1 + +div.dataset.nr2 ); //46

Jeżeli z jakiś powodów chcielibyśmy przechowywać w atrybutach wartości tablic czy obiektów, musimy je skonwertować na format JSON:


//bez konwersji - źle
div.dataset.table = [1,2,3,4,5];
div.dataset.ob = {name : "Marcin"};

console.log(div.dataset.table); //"1,2,3,4,5"
console.log(div.dataset.ob); //"[object Object]"

//z konwersją - lepiej
div.dataset.table = JSON.stringify( [1,2,3,4,5] );
div.dataset.ob = JSON.stringify( {name : "Marcin"} );

console.log( JSON.parse(div.dataset.table) ); //["1,2,3,4,5"]
console.log( JSON.parse(div.dataset.ob) ); //{name : "Marcin"}

Tak skonwertowane obiekty nie mogą zawierać żadnych funkcji, dlatego dataset raczej nie nadaje się do przechowywania bardzo skomplikowanych danych, a tylko prostych tekstów lub wartości wykorzystanych do stylowania.

Atrybuty i właściwości

HTML to tak naprawdę kawałek tekstu, który napisaliśmy w dowolnym edytorze. Przeglądarka analizuje taki tekst, przetwarza, a następnie dla każdego elementu w HTML tworzy jego odzwierciedlenie w postaci odpowiedniego obiektu:


<a id="link" href="https://google.pl" target="_blank"> było - zamykam temat </a>

const link = document.querySelector("#link");
console.dir(link);

Obiekt taki automatycznie dostał wiele właściwości. Wśród nich są te, które odpowiadają atrybutom z html - np. id, href ale też takie które element danego typu potencjalnie może mieć. W powyższym przykładzie mamy link, więc ma on dla przykładu atrybut href. Nasz obiekt w JS także będzie miał taką właściwość.

html i js

<img src="obrazek.jpg" alt="obrazek" title="Fajny obrazek">

const img = document.querySelector("img");

img.src //"obrazek.jpg"
img.alt //"obrazek"
img.title //"Fajny obrazek"

Niektóre wartości są dodatkowo przeliczane:


<a id="link1" href="./kontakt.html"> kontakt </a>
<a id="link2" href="https://google.pl"> było - zamykam temat </a>

const link1 = document.querySelector("#link1");
console.log( link1.href ); //file:///C:/Users/kartofelek/Desktop/kontakt.html

const link2 = document.querySelector("#link1");
console.log( link2.href ); //https://google.pl

Atrybuty i właściwości są ze sobą scalone. Oznacza to, że przykładowo zmieniając atrybut href, zmienimy też właściwość href i vice wersa.


<a id="link" href="https://google.pl"> było - zamykam temat </a>

const a = document.querySelector("a");

a.href = "strona.html"
console.log(a.getAttribute("href")); //strona.html

const a = document.querySelector("a");

a.setAttribute("href", "strona.html"); //strona.html
console.log(a.getAttribute("href")); //file:///C:/Users/kartofelek/Desktop/test/strona.html
console.log(a.href);

Tu pojawia się też pewna ciekawostka.

Dla inputów w formularzach istnieją dwie właściwości - defaultValue i value.

Wchodząc na stronę z formularzem, część pól może być już domyślnie wypełniona. Mają one wtedy ustawiony atrybuty value np:


<input type="text" value="Piotrek">

Atrybut ten jest zmapowany z właściwością defaultValue, co oznacza, że jego wartość możemy odczytywać ale i zmienić na dwa sposoby:


input.setAttribute("value", "nowa-wartość");
input.defaultValue = "nowa-wartość"

Wartość value natomiast zawiera zawsze aktualną wartość pola. Po wejściu na stronę wartość ta będzie identyczna jak defaultValue. Gdy jednak użytkownik zacznie ręcznie edytować dane pole, właściwość value automatycznie zaktualizuje swoją wartość, natomiast atrybut (a co za tym idzie defaultValue) pozostanie bez zmian.

Dla nas oznacza to tyle, że jeżeli chcemy odczytać początkową wartość pola - użyjmy defaultValue. Natomiast jeżeli chcemy pobrać aktualną wartość pola - value. Podobna sytuacja będzie się dziać w przypadku checkboxów i radio gdzie mamy właściwości defaultChecked i checked.

Dla bardzo dociekliwych mechanizm ten opisany jest w dokumentacji.

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.

Menu