Poznaj querySelector i przestań używać getElementsBy…

btw – kod może Cię zaskoczyć!

Spis treści

  1. Wprowadzenie
  2. QuerySelector/querySelectorAll
    1. Kilka słów o querySelector
    2. Słówko o querySelectorAll
    3. Dwa słowa o pewnej nieścisłości
    4. Kod przykładu na Github
    5. Klikalny przykład na Github Pages
  3. Dlaczego querySelector jest lepszy od innych metod?
  4. Podsumowanie

Wprowadzenie

Lat temu bardzo dużo główną przewagą jQuery nad czystym JS było to, że ujednolicała i upraszczała zachowanie pomiędzy przeglądarkami. JS jednak zaczął nadrabiać stracone lata, modernizując swoje API, większy nacisk również został położony na kompatybilność między przeglądarkami.

Jedną z rzeczy które najbardziej lubię w jQuery jest możliwość używania selektorów do pobierania elementu, np $('.form .element:not(.inactive)').

I choć JavaScript od wielu lat daje możliwość używania selektorów przy pobieraniu elementów DOM za pomocą querySelector/querySelectorAll, bardzo często widzę użycia innych metod pobierania elementów, jak getElementById czy getElementsByTagName.

W tym wpisie będę chciał przekonać Cię, że querySelector/querySelectorAll to zdecydowanie lepszy wybór 🙂

QuerySelector/querySelectorAll

Kilka słów o querySelector

Metoda ta zwraca pierwszy element pasujący do selektora.

Zwrócony element jest typu Element, interfejs ten udostępnia m.in metody:

  • closest, która zwróci pierwszego rodzica pasującego do selektora
  • animate, pozwalającą utworzyć oraz uruchomić animację na wybranym elemencie
  • requestFullscreen, która pokaże wybrany element na pełnym ekranie

Istotnym elementem jest to, że metody querySelector i querySelectorAll można uruchamiać również na obiekcie typu Element – co spowoduje wyszukanie po danym selektorze wewnątrz wybranego elementu. Wiąże się z tym pewna nieścisłość – ale o tym dalej.

Słówko o querySelectorAll

Ponieważ querySelector zwraca tylko pierwszy pasujący element, potrzebujemy metody która zwróci nam wszystkie pasujące elementy. Tą metodą jest querySelectorAll.

Zwraca ona obiekt NodeList – możesz iterować po tym obiekcie jak po tablicy metodą forEach bądź składnią for..of, aczkolwiek jeśli chcesz mieć pełną paletę operacji na tablicach możesz przetransformować obiekt do prawdziwej tablicy:

const array = [...document.querySelector('.items')];

Dwa słowa o pewnej nieścisłości

querySelector pozwala stosować selektory a także przeszukiwać DOM relatywnie w stosunku do elementu, jest jednak jedno „ale”.

Jeżeli przeszukujemy DOM relatywnie spodziewalibyśmy się, że znalezione elementy będą musiały spełniać warunek selektora wewnątrz elementu. Brzmi to może dziwnie 🙂 Ale spójrzmy na kod:

<div class="parent">
  <div id="element">
    <div class="child"></div>
  </div>
</div>

Mając taki kod JS:

const element = document.querySelector('#element');
const child = element.querySelector('.parent .child');

Spodziewałbym się zwrócenia wartości null, ponieważ w divie o id element nie ma elementu z klasą child wewnątrz elementu z klasą parent.

Co zaskakujące jednak, zostanie zwrócony div z klasą child – ponieważ domyślnie cała hierarchia DOM jest brana pod uwagę.

Może to być uznane za nienaturalne – jak więc uzyskać naturalne zachowanie? Poprzez dodanie do selektora pseudoklasy :scope.

Spójrzmy na taki kod HTML:

<div class="parent">
  Parent
  <div class="child child-1">
    Child outer
    <div class="child child-2">
      Child inner
      <div class="child child-3">
        Child
        <div class="parent child-1">
          Child but also a parent
          <div class="child child-2">
            Child
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Powtarza się tam selektor .parent .child, który będziemy chcieli sprawdzić.

Stworzymy selektor który będzie miał formę albo

':scope .parent .child'

I ten selektor będzie sprawdzany tylko w hierarchii wewnątrz elementu, oraz selektor z zachowaniem domyślnym:

'.parent .child'

Elementem w stosunku do którego będziemy przeszukiwać dom będzie element z tekstem Child outer. Kliknięcie na przycisk będzie wywoływać funkcję, która do pasujących elementów dodawać będzie czarne tło:

const child1 = document.querySelector('.child-1');
    
document.querySelector('#select').addEventListener('click', () => {
  const scoped = document.querySelector('#scoped').checked;
  const selector = `${scoped ? ':scope ' : ''}.parent .child`;
  const elements = [...child1.querySelectorAll(selector)];
  elements.forEach(element => element.classList.add('black'));
});

Jaki będzie efekt dla wywołania funkcji bez zaznaczonego checkboxa scoped? Zaznaczonych zostanie wiele elementów, ponieważ pod uwagę wzięta będzie klasa parent będąca poza elementem w stosunku do którego szukamy:

Natomiast gdy zaznaczymy checkbox zaznaczy się tylko jeden element:

Ponieważ dodana pseudoklasa wymusza branie pod uwagę tylko hierarchię wewnątrz elementu.

Kod przykładu na Github

Pełny kod przykładu można znaleźć pod adresem:

https://github.com/radek-anuszewski/queryselector-scope-demo

Klikalny przykład na Github Pages

Możesz przeklikać przykład pod adresem:

https://radek-anuszewski.github.io/queryselector-scope-demo/index.html

Dlaczego querySelector jest lepszy od innych metod?

Moim zdaniem oczywiście 🙂 A więc czemu tak uważam?

Po pierwsze, mamy bardziej czytelne i jednorodne API. Istnieje jedna metoda zamiast kilku:

  1. getElementById
  2. getElementsByClassName
  3. getElementsByTagName

Pierwsza z powyższych zwraca pojedynczy element – co jest oczywiście logiczne biorąc pod uwagę, że id powinno być unikalne, pozostałe 2 zwracają listę elementów.
Uważam że zachowanie querySelector i querySelectorAll jest czytelniejsze – chcesz pierwszy pasujący do selektora element? Użyj tego pierwszego. Chcesz listę elementów? Użyj drugiego.

Używanie selektorów pozwala nam reużywać wiedzy którą nabyliśmy przy nauce / pracy w CSS. I odwrotnie – jeśli poznajemy selektory pod kątem metody querySelector wiedza ta będzie mieć również zastosowanie przy stylowaniu aplikacji.

No i najważniejsze na koniec, coś co jest oczywiste – selektory 🙂 Dzięki temu, bez konieczności użycia jQuery bądź innej biblioteki czy konieczności odfiltrowywania elementów w kodzie, możemy bardzo szczegółowo przeszukiwać DOM.

Podsumowanie

Oczywiście dużą rolę w wyborze sposobu odpytywania DOM będą odgrywać osobiste preferencje. Niektórzy uznają dedykowane metody za lepsze – jako bardziej ekspresyjne niż querySelector, którego nazwa na pierwszy rzut oka niewiele mówi.
Osobiście uważam, że querySelector ma znacznie większe możliwości niż inne metody a także jest bardziej minimalistyczna. Zamiast 3 metod mamy 2, które dodatkowo używają zasad znanych z CSS – dlatego myślę, że powinny być preferowanym sposobem dostępu po DOM.

Sprawdź jak ograniczyć ilość event listenerów w JS!

Wprowadzenie

A po co ograniczać ilość event listenerów? Powodów jest kilka:

  1. Szybsze budowanie się UI. Mniej listenerów do podpięcia to po prostu mniej kodu do wykonania. To także często mniej operacji na DOM – aby podpiąć listener do elementu musimy przecież pobrać go np za pomocą querySelector.
  2. Płynniejsza obsługa UI. Mniej listenerów to mniej nasłuchiwania, szczególnie jeśli np w podpinamy bardzo dużo eventów mouseenter/mouseleave do np tabelki.

W jaki sposób możemy więc ograniczyć ilość listenerów? To zależy od sytuacji, ale jeden ze sposobów może być wykorzystany nader często – mianowicie podpinanie listenerów na rodzicu zamiast na poszczególnych elementach.

W tym artykule porównamy szybkość podpinania listenerów w 2 przypadkach – gdy podepniemy jeden listener to całej tablicy a także gdy podepniemy listener do każdego przycisku w wierszach tablicy. Tablicę wypełnimy tysiącem wierszy z których każdy będzie miał 1 przycisk do usunięcia tego wiersza.

Czytaj dalej Sprawdź jak ograniczyć ilość event listenerów w JS!

Sprawdź jak dzięki ImageCapture zrobisz zdjęcie prościej w swojej aplikacji

Spis treści

  1. Wprowadzenie
  2. Kod bazowy dla porównania ImageCapture vs canvas
  3. Robienie zdjęcia
    1. Stary sposób z canvas
    2. Nowy sposób z ImageCapture
  4. Wsparcie ImageCapture
  5. Kod przykładu na Github
  6. Klikalny przykład na Github Pages
  7. Podsumowanie

Wprowadzenie

We wszystkich aplikacjach WWW w których pracowałem do tej pory robienie zdjęcia polegało na podpięciu się do wideo z kamery i przechwyceniu pojedynczej klatki strumienia. Klatka ta była potem rysowana na elemencie <canvas> a następnie zapisywana jako blob.

To podejście jest hakiem, wymaga nadmiarowych elementów. Niedawno natknąłem się na dedykowane przeglądarkowe API ImageCapture, opisane m.in. na Mozilli. Pozwala ono zrobić zdjęcie w znacznie prostszy sposób, bez elementów pośrednich i obejść.

Sprawdźmy więc czym różni się nowe podejście od starego.

Czytaj dalej Sprawdź jak dzięki ImageCapture zrobisz zdjęcie prościej w swojej aplikacji

Obsługuj pliki lepiej dzięki File System Access Api

Spis treści

  1. Wprowadzenie
  2. Notka o modułach
  3. Obecny sposób obsługi plików
    1. Implementacja
    2. Niedogodności
  4. Nowy sposób obsługi plików z File System Access API
    1. Implementacja
    2. Wątpliwości dotyczące File System Access API
  5. Kod przykładu na Github
  6. Klikalny przykład na Github Pages
  7. Podsumowanie

Wprowadzenie

Kiedy ostatnio spojrzałeś w katalog plików pobranych na Twoim dysku? Czy przeszukiwanie go zawsze jest koszmarem, z ogromną ilością duplikatów i plikami nazwanymi w taki sposób: somefile.txt, somefile (1).txt, somefile (2).txt, somefile (2) (1).txt itd.

Jeżeli miałeś przyjemność pracować na dokumentach do których zmiany dodaje kilka osób to praca wygląda często tak:

  1. Pobierasz plik somedocs.txt bo chcesz coś dodać
  2. Wpisujesz ważne rzeczy i wrzucasz na Slacka/Teamsy.
  3. Ktoś doda zmiany, pobierasz plik i trafia on do katalogu Pobrane, ale ma nazwę somedocs (1).txt żeby nie nadpisać obecnego pliku
  4. Wrzucasz, ktoś bierze pliczek, znowu coś dopisuje
  5. Pobierasz, tym razem ma nazwę somedocs (1) (1).txt
  6. Robi się nieporządek

I jestem w stanie wymienić bez zastanowienia sytuację, gdy spotkałem się z takim namnażaniem się chaosu 🙂 Mianowicie:

Pliczek z pytaniami do rekrutacji – nie chcesz by ten plik był dostępny dla wszystkich, by współpracownicy nie wrzucali go swoim znajomym aplikującym do firmy w której pracujesz, a fajnie mieć plik z którego możesz brać pytania.

Na szczęście pojawia się API, które pozwoli nam zapanować nad chaosem – File System Access API. Pozwala ono między innymi na zapis plików na dysku z wyborem nazwy – w analogiczny sposób jak zapisują pliki natywne aplikacje.

W tym poście porównamy obie wersje.

Czytaj dalej Obsługuj pliki lepiej dzięki File System Access Api

React Portals i podejścia alternatywne

Spis treści

  1. Wprowadzenie
  2. React Portals
  3. Alternatywy dla React Portals
    1. React Context
    2. React Redux
  4. Kod przykładu na Github
  5. Klikalny przykład na Github Pages
  6. Podsumowanie

Wprowadzenie

Portale w React są funkcjonalnością, na którą natkniesz się prawdopodobnie na samym końcu – po przekopaniu całego internetu w poszukiwaniu rozwiązania. Renderowanie zawartości w innym miejscu niż logicznie element należy wydaje się nie mieć sensu – pomaga jednak rozwiązać problemy z z-index czy overflow na jakie możesz natknąć się przy próbach wyświetlenia treści. Choć Portal niekoniecznie będzie pierwszym wyborem – często może okazać się znacznie szybszym rozwiązaniem niż refaktor lub inne bardziej oczywiste opcje – poznajmy więc jak one działają.

Czytaj dalej React Portals i podejścia alternatywne

Notifications API – powiadomienia jako sposób nawiązywania relacji z użytkownikiem

Spis treści

  1. Wprowadzenie
  2. Wyświetlanie powiadomień
    1. W jakim momencie można wyświetlać użytkownikowi powiadomienia?
    2. Jak nie być natrętnym z wyświetlaniem powiadomień?
    3. Kod przykładu na Github
    4. Klikalny przykład na Github Pages
  3. Podsumowanie

Wprowadzenie

Pokazywanie powiadomień jest przydatne w wielu sytuacjach – idealnym przykładem są czaty, gdzie w oczekiwaniu na odpowiedź drugiej strony możemy zająć się czymś innym niż gapieniem się w ekran 🙂

Dodatkowo, w przeglądarkach opartych na Chromium powiadomienia wyglądają i zachowują się jak notyfikacje systemowe, co dodatkowo podnosi ich atrakcyjność i przyciąga uwagę bardziej niż zwykłe powiadomienia przeglądarkowe. Na Windows 10 powiadomienie takie odtwarza dźwięk, dzięki czemu będzie łatwiej zauważone przez użytkownika.

Czytaj dalej Notifications API – powiadomienia jako sposób nawiązywania relacji z użytkownikiem