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 debugera F12 i zostaw konsole widoczną, bo jeszcze nam się przyda.

Pobrałem za ciebie powyższy przycisk. Otwórz debuger (F12) i rozwiń wypisany element.


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

Powyższy screen to tylko urywek całości.
Obiekt taki ma bardzo dużo właściwości i metod.
Część z nich wykorzystywana jest dla eventow takich jak kliknięcie, zaznaczenie, część przechowuje pozycję na stronie, typ elementu, jego rozmiary itp.
Większości z tych metod i właściwości na co dzień się nie wykorzystuje, ale jeżeli trafi się taka konieczność, wiedz po prostu gdzie szukać tego co cię interesuje (rozwijając pobrany element i wypisując go w konsoli).

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

innerHTMLzwraca lub ustawia kod HTML danego element
outerHTMLzwraca lub ustawia kod HTML wraz z tagiem
innerTextzwraca lub ustawia tekst znajdujący się w elemencie (bez html)
tagNamezwraca nazwę tagu
setAttributeustawia atrybut elementu
getAttributepobiera atrybut elementu
hasAttributesprawdza czy element ma dany atrybut
datasetzwraca (obiekt) dataset, który przetrzymuje customowe atrybuty (data-...).

innerHTML i outerHTML

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


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

console.log( btn.innerHTML ); //<span>Kliknij mnie!</span>

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

Za pomocą innerHTML możemy wstawiać całe kawałki html html dynamicznie:


<div class="test-cnt">Tutaj zaraz coś wstawimy</div>

<button class="button" type="button">Stwórz dynamicznie html</button>

document.querySelector('.button').addEventListener('click', function() {
    let html = '';
    html += '<div class="module">';
    html += '   <div>To jest <strong>dynamicznie wstawiony html</strong>:</div>';
    html += '   <button class="button">Klik!</button>';
    html += '   <button class="button">Inny klik!</button>';
    html += '</div>';

    document.querySelector('.test-cnt').innerHTML = html;
});
Tutaj zaraz coś wstawimy

Podobnie możemy też czyścić całe elementy:


document.querySelector('div.to-clean').innerHTML = "";

Przy czym nie zawsze jest to najlepsza metoda. Więcej na ten temat dowiesz się tutaj.

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="button" type="button"><span>Kliknij mnie</span></button>

innerText i textContent

Właściwości innerText i textContent działają podobnie do powyższej innerHTML, z tym że zwracają 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

Podstawowa różnica między innerText a textContent jest taka, że innerText zwraca tekst po zaaplikowaniu stylów, a textContent pomija style. Co to oznacza? Jeżeli na jakiś element w tekście zostanie poprzez style nadade visibility:hidden lub display:none, wtedy innerText nie zwróci ukrytych części:


.button-text {}
.button-text-hide {
    visibility:hidden;
}
.button-text-none {
    display:none;
}

<div class="test">
    <span class="button-text-none">Ukryty</span>
    <span>Obok mnie są dwa niewidoczne spany</span>
    <span class="button-text-hide">Niewidoczny</span>
</div>

<button class="button" type="button">
    Kliknij
</button>

const btn = document.querySelector('.button');
const divTest = document.querySelector('.test');

btn.addEventListener('click', function() {
    console.log('innerHTML: ', divTest.innerHTML);
    console.log('innerText: ', divTest.innerText);
    console.log('textContent: ', divTest.textContent);
});
Ukryty Obok mnie są dwa niewidoczne spany Niewidoczny

tagName

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


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

Znowu - do czego to może się przydać? Bardzo często będziesz podpinał dane zdarzenie dla wielu typów elementów na raz. Po kliknięciu na taki element chcesz wiedzieć co kliknąłeś, by wykonać odpowiednie działania:


const elements = document.querySelector('div.test, .button, h2.title');
for (let i=0; i<elements.length; i++) {
    elements[i].addEventListener('click', function() {
        console.log(this.tagName);
        if (this.tagName.toLowerCase() === 'button') {
            ...
        }
        if (this.tagName.toLowerCase() === 'div') {
            ...
        }
        if (this.tagName.toLowerCase() === 'h2') {
            ...
        }
    });
}

setAttribute, getAttribute, hasAttribute i removeAttribute

Metody setAttribute(nazwaAtrybutu, wartosc) i getAttribute(nazwaAtrybutu) służą do ustawiania i pobierania atrybutów HTML danego znacznika.

Metoda removeAttribute(nazwaAtrybutu) służy do usunięcia atrybutu.

Metoda getAttribute(nazwaAtrybutu) wymaga tylko jednego parametru, którym jest nazwa pobieranego atrybutu. Jeżeli dany atrybut nie zostanie odnaleziony, metoda zwróci null:


<form id="contactForm" class="form-contact" method="post" action="script.php">
    ...
</form>

const form = document.querySelector('.form-contact');

const formID = form.getAttribute('id');
const callType = form.getAttribute('method');
const callUrl = form.getAttribute('action');

Metoda setAttribute(nazwaAtrybutu, wartość) wymaga podania zarówno nazwy atrybutu, jak i jego nowej wartości:


const submit = form.querySelector(':scope :submit');

submit.setAttribute('disabled', 'disabled');

Za pomocą tej metody nie musimy ustawiać tylko domyślnych atrybutów, ale i nasze własne:


const tooltip = document.querySelectorAll('.tooltip');

for (let i=0; i<tooltip.length; i++) {
    tooltip[i].setAttribute('data-text', tooltip[i].textContent);
}

Aby usunąć atrybut, skorzystamy z metody removeAttribute():


const inputText = document.querySelectorAll('[readonly]'); //pobieram pola readonly
inputText.removeAttribute('readonly'); //usuwam ten atrybut - od tej pory można je wypełniać

Jeżeli chcemy sprawdzić czy dany element ma konkretny atrybut, wtedy możemy skorzystać z metody hasAttribute(nazwaAtrybutu). Metoda ta sprawdza czy dany atrybut istnieje zwracając true/false:


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

//hasAttribute zwróci true/false
if (img.hasAttribute('title')) {
    console.log(img.getAttribute('title'));
} else {
    console.log('Zdjęcie nie ma atrybutu title');
}

dataset

Właściwość dataset zwraca nam obiekt, który służy do manipulowania customowymi atrybutami (czyli takimi jaki ustawiliśmy przed chwilą dla tooltipów). Atrybuty takie zaczynają się od słowa data-


<span class="tooltip" data-default-text="..." data-position="top">
    Help
</span>

W powyższym przykładzie mamy 2 customowe atrybuty: data-default-text i data-position

Jeżeli teraz chcielibyśmy je odczytać użyjemy konstrukcji:


const tooltip = document.querySelector('.tooltip');
console.log(tooltip.dataset.defaultText);
console.log(tooltip.dataset.position);

Zauważ, jak odwołaliśmy się do nazw naszych parametrów.
Początek data- został pominięty, a zapis default-text zmieniliśmy na pisany stylem camelCase czyli defaultText. Podobna zamiana działa też przy stylach, gdzie dla przykładu background-color jest zamieniany na backgroundColor.


<div class="module"
    data-my-custom-data="..."
    data-style="..."
    data-module-type="..."
>
   ...
</div>

const tooltip = document.querySelector('.module');
console.log(tooltip.dataset.myCustomData);
console.log(tooltip.dataset.style);
console.log(tooltip.dataset.moduleType);

tooltip.style.backgroundColor = "red";

Jeżeli teraz chcielibyśmy ustawić jakiś własny customowy atrybut, lub zmienić istniejący, wystarczy że ustawimy im nową wartość:


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.moduleType = "big" //utworzy atrybut data-module-type="big"

tooltip.dataset.title = "important"; //data-title
tooltip.dataset.footerStyle = "bottom"; //data-footer-style

//to samo możemy uzyskać za pomocą getAttribute i setAttribute (mini-mini wolniejsze)
tooltip.getAttribute('data-style');
tooltip.setAttribute('data-title', "important");
tooltip.setAtribute('data-footer-style', "bottom");

Użycie konstrukcji element.dataset.footerStyle="bottom" utworzy w takim elemencie atrybut data-footer-style, co jest równoznaczne z zastosowaniem konstrukcji element.setAttribute('data-footer-style', 'bottom')

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


<button type="button" id="myButton" data-id="23" data-ob='{"name":"John"}'>
    Kliknij mnie
</button>

typeof myButton.dataset.id === 'string' //true
typeof myButton.dataset.ob === 'string' //true

Można to obchodzić przez zastosowanie odpowiednich metod konwertujących takich jak Number czy JSON.parse:


const ob = JSON.parse(myButton.dataset.ob);
const id = Number(myButton.dataset.id);

Jeżeli chcielibyśmy przechowywać w dataset obiekt, wtedy musimy skorzystać z JSON.stringfity:


const ob = {
    name : "Marcin",
    lastname : "Domanski"
}

myButton.dataset.ob === JSON.stringfity(ob);

Atrybuty kontra właściwości

Jak ci pokazałem w przykładzie z ID, "właściwość" tą możemy ustawić zarówno jako właściwość obiektu na którym operujemy, oraz za pomocą powyżej opisanych metod - jako atrybut HTML tego elementu.

Tutaj małe wyjaśnienie. Struktura HTML jest odzwierciedlona w drzewie DOM. Drzewo takie składa się z elementów, ktore są niczym innym jak obiektami. Jeżeli teraz wypiszemy pobrany element w konsoli np. za pomocą kodu:


const button = document.querySelector('button');
console.dir(button);

zobaczymy, że pobrany element jest obiektem jakiegoś typu - np. HTMLButtonElement (czyli został stworzony na bazie konstruktora HTMLButtonElement). Obiekt taki ma w sobie wiele właściwości. Wśród nich są te, które odpowiadają wartościom atrybutów z html - np. id, title, type. Jakie są to właścwiości zależy od typu danego obiektu.
Dla przykładu powyżej wypisany button to obiekt typu HTMLButtonElement. Nie posiada on właściwości alt, bo normalnie atrybut taki jest nieprawidłowy dla buttonów czy dla przykładu for, bo atrybut ten jest przeznaczony dla labeli.
Gdybyśmy podobnie wypisali w konsoli inny pobrany ze strony element - np. img, otrzymalibyśmy w konsoli obiekt HTMLImageElement, który miałby właściwość alt, src, width, height (i wiele innych), ale już nie właściwość type, ponieważ właściwość ta jest przeznaczona dla buttonów.

Ogólnie więc obiekty z których składa się drzewo DOM zawierają w sobie właściwości, które odpowiadają standardowym atrybutom dla danego typu elementu:


button.type //button
button.className //button
button.id //testBtn
button.name //ourBtn
button.for //undefined

img.type //undefined
img.src //someImg.jpg
img.width //200
img.title //"Nasza testowa grafika"

W większości przypadków wartości takich obiektów są automatycznie aktualizowane jeżeli dana wartość elementu na stronie się zmieni. W przypadku odwoływania się do HTML atrybutów zmiany czasami nie są autoamtycznie aktualizowane:


<input type="text" id="testValue" value="Domyślna wartość" />

document.querySelector('#testValue').addEventListener('click', function() {
    const input = document.querySelector('#testValue');
    console.log("input.getAttribute('value'): ", input.getAttribute('value'));
    console.log("input.value: ", input.value);
});

Zaleca się by do standardowych atrybutów odwoływać się przez właściwości obiektu. Metody getAttribute i setAttribute powinniśmy stosować do atrybutów, które nie występują standardowo w elementach danego typu, lub do takichm które dla przykładu chcielibyśmy potem wykorzystać w CSS. Będą to głównie atrybuty aria- lub nasze własne atrybuty data-... (do których mozemy też użyć dataset).

Sprawdź to w poniższym przykładzie. W HTML pole ma wartość atrybutu value ustawioną na Domyślna wartość. Wpisz coś w pole i kliknij w button spraszając w konsoli wynik.

Trening czyni mistrza

Poniżej zamieszczam kilka zadań, które w ramach ćwiczenia możesz wykonać:

  1. Ściągnij sobie przykładową stronę klikając prawym przyciskiem tutaj i wybierając zapisz jako.
    Zadania wykonuj w skryptach inline pisanych na dole kodu strony.
  2. Pobierz listę .list i podstaw ją pod zmienną.
  3. Korzystając z powyższej zmiennej dodaj liście klasę preety
  4. Pierwszemu LI listy dodaj klasę first
    Ostatniemu LI listy dodaj klasę last
    
                const list = document.querySelector('.list');
                list.classList.add('preety');
    
                const li = list.querySelectorAll('li');
                li[0].classList.add('first');
                li[li.length-1].classList.add('last');
    
                //lub
                list.firstElementChild.classList.add('first');
                list.lastElementChild.classList.add('last');
            
  5. Po najechaniu na każde LI listy wyświetl tooltip, który będzie zawierał tekst z środka. Dla tooltipa wykorzystaj atrybut title
    
                    const list = document.querySelector(".list");
                    list.classList.add("preety");
    
                    const li = list.querySelectorAll("li");
    
    
                    [].forEach.call(li, function(el) {
                        el.setAttribute("title", el.innerText);
                    });
    
                    //lub w ES6
                    [...li].forEach(el => {
                        el.setAttribute("title", el.innerText);
                    })
    
                    //lub e ES6
                    for (const el of li) {
                        el.setAttribute("title", el.innerText);
                    }
                
  6. Trzeciemu elementowi w liście ustaw kolot tła na kolor #f70c74. Wykorzystaj do tego właściwość style.
    
                    const list = document.querySelector(".list");
                    const li = list.querySelectorAll("li");
    
                    li[2].style.backgroundColor = "#f70c74";
                    //lub
                    li[2].style.setProperty("background-color", "#f70c74");
                
  7. Trzeciemu elementowi ustaw text na "Jestem czerwonym elementem"
    
                    const list = document.querySelector(".list");
                    const li = list.querySelectorAll("li");
                    li[2].innerText = "Jestem czerwonym elementem";