chevron-left chevron-right

[JS] Wykrywanie selektora wybranego elementu strony za pomocą JavaScript

Właśnie przygotowuję się do tworzenia aplikacji w której może być przydatne wykrywanie selektora CSS elementu znajdującego się na stronie i postanowiłem się podzielić z Tobą swoim pomysłem. Możesz się zastanawiać, w jakich sytuacjach może się przydać coś takiego? Skoro do tego równie dobrze można wykorzystać Web Developer Tools zaimplementowane w każdej przeglądarce. To jest dobre pytanie.

Taka funkcjonalność może się przydać, gdy zależy nam na tym, aby użytkownik był w stanie wskazać jakiś element na stronie lub aplikacji a następnie móc odpowiednio przekazać informację dotyczącą tego elementu, na przykład jakąś konfigurację bądź feedback. Nie każda osoba jest na tyle obznajomiona z Narzędziami dla Webmasterów aby móc swobodnie się nimi posługiwać.

Detektor selektorów CSS

Do zobrazowania przykładowego kodu JS do generowania selektorów CSS na podstawie wybranego elementu strony zamierzam wykorzystać możliwości pochodzące z najnowszych wersji języka JavaScript (JS >= ES6) . Jeśli będzie istniała potrzeba przedstawienia wersji w poprzedniej generacji języka (ES5) to daj mi znać w komentarzu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function getCssSelector(element, callback) {
    let fullSelector = '';
    // funkcja sprawdzająca czy wybrany obiekt jest typu undefined
    const isUndefined = (item) => typeof item === 'undefined';
    // funkcja generująca selektor dla elementu
    const generateSelector = function (el, cb) {
        // najpierw pobieramy nazwę tagu HTML, np. div
        let elementSelector = el.tagName.toLowerCase();
 
        // jeśli element posiada atrybut id
        if (!isUndefined(el.id) && el.id.length) { {
            // tworzymy selektor id
            elementSelector = elementSelector + '#' + el.id;
        }
 
        // jeśli element posiada klasy CSS
        if (!isUndefined(el.className)) {
            // tworzymy selektor klas CSS i dołączamy do poprzednio utworzonej części selektora
            elementSelector = [elementSelector, [...el.classList].join('.')].join('.');
        }
 
        // dołączamy wygenerowany selektor elementu do pełnego selektora wybranego przez nas elementu
        fullSelector = elementSelector + ' ' + fullSelector;
 
        // sprawdzamy czy element ma rodzica
        if (!isUndefined(el.parentNode.tagName)) {
            // generujemy selektor rodzica
            generateSelector(el.parentNode, cb);
        } else {
            // zwracamy wygenerowany selektor do funkcji zwrotnej (callback)
            cb(fullSelector);
        }
    };
 
    // generujemy selektor dla wybranego przez nas elementu
    // i przekazujemy callback
    generateSelector(element, callback);
}

W kodzie powyżej zamieściłem komentarze opisujące co się dzieje po kolei. Warto zwrócić uwagę na użycie operatora spread - [...el.classList]], który pozwala robić rzutowanie na tablicę każdego obiektu niebędącego tablicą, ale mającego własność length. Bardzo przydatna rzecz.

Oprócz operatora spread wykorzystałem też tzw. arrow functions, czyli funkcje ze strzałką, które zachowują kontekst miejsca w którym zostały wywołane. Jeśli chcesz się dowiedzieć więcej na temat nowych rzeczy użytych w kodzie powyżej, to daj znać w komentarzu.

Na koniec wystarczy teraz wywołać funkcję z odpowiednimi parametrami:

1
getCssSelector(document.querySelector('#ezs-studio-preview'), path => console.log('path', path));

Jako pierwszy parametr, przekazałem wybrany element. W tym przypadku jest to odrobinę niefortunne, bo już znamy odpowiedni selektor (którym jest id i który powinien być unikalny), ale w przypadku gdy interesuje nas selektor elementu, który właśnie został kliknięty to zamiast document.querySelector('#ezs-studio-preview') wystarczy dać event.target i już selektor nie jest taki oczywisty.

Jako drugi parametr przekazano funkcję, która ma przyjąć wygenerowany selektor jako swój parametr. W przykładzie powyżej, w konsoli zostanie wypisany wygenerowany selektor.

Przykładowy wygenerowany selektor może wyglądać następująco: html.ez-platformui-app-page.yui3-js-enabled.ez-platformui-app-ready body.ez-platformui-app-body.yui3-skin-platformui div#yui_3_18_1_1_1487242089504_294.ez-platformui-app.pure.is-menu-hidden.is-universaldiscovery-hidden.is-contentpeek-hidden.is-confirmbox-hidden.is-languageselectionbox-hidden.yui3-app.is-app-open.is-studio-loaded div.ez-mainviews.pure-g div.ez-view-container.pure-u.yui3-app-views div#yui_3_18_1_1_1487242089504_2335.ez-view-browserview div.ezs-appview.pure-g div.ezs-appview__workspace.pure-u iframe#ezs-studio-preview.ezs-appview__workspace__preview. Jest on bardzo długi i na pewno dałoby się go uprościć, ale nie to było celem tego wpisu.

Podsumowanie

W tym krótkim wpisie przedstawiłem sposób na wykorzystanie najnowszej składni JS do rozwiązania problemu jaki miałem. Mam nadzieję, że przygotowany przeze mnie kawałek kodu okaże się przydatny w Twoim projekcie. Zapraszam do komentowania 🙂

  • A co jak mam div[data-action=”costam”]? :v

  • Tego przypadku nie uwzględniłem w skrypcie. Nie sądzę by to było jakieś większe wyzwanie, tym bardziej jak się ma dostęp do `dataset`

  • Hah, racja – zawsze zapominam o dataset. Już myślałem o parsowaniu element.attributes…

  • Mariu

    Dawno nie miałem do czynienia z JS, zatrzymałem się na ES5,
    czemu path => console.log(‚path’, path) ,
    a nie path => console.log( path) ?

  • Żeby wyświetlić przed ścieżką słówko ‚path’, dla większej czytelności 😉