NodeFilter#
NodeFilter.acceptNode()#
Metoda acceptNode()
jest dodatkową opcją testującą używaną przez filtr w obiektach typu NodeIterator
oraz TreeWalker
.
Interfejs NodeFilter jest specyficzny ponieważ jest typu zwrotnego (callback interface). W praktyce oznacza to tyle, że nie istnieje żadna gotowa metoda pozwalająca utworzyć taki obiekt. To my musimy utworzyć dowolny obiekt JS i rozszerzyć go o pewne elementy (w tym wypadku metodą o nazwie "acceptNode
"). W praktyce całość można rozwiązać na kilka sposobów, dlatego w dalszej części przedstawię ich najpopularniejsze warianty.
Opis działania#
Samo wywołanie i poszczególne jego części najlepiej objaśnić na zapisie składniowym:
// Wariant obiektowy (obiekt klasyczny) var filtr1 = { acceptNode: function(node){ kod funkcji } }; // Wariant funkcyjny (obiekt funkcyjny) var filtr2 = function(node){ kod funkcji };
gdzie poszczególne człony oznaczają:
- filtr1, filtr2 - referencja do filtru zadeklarowanego na dwa różne sposoby.
- node - pierwszy parametr w metodzie, pod którym dostępny będzie testowany węzeł w kodzie funkcji (nazwę można podać dowolną).
- kod funkcji - właściwy kod dla funkcji testującej.
Najlepiej omówić wszystko od podstaw jeszcze raz. Maska bitowa jest tylko wstępną filtracją, gdyż pozwala z grubsza przefiltrować węzły jedynie pod kątem ich typu. Filtr pozwala wprowadzić dodatkową filtrację węzłów z dowolnymi kryteriami testującymi. Korzeń, maskę bitową oraz filtr przekazujemy bezpośrednio do metod Document.createNodeItearor()
oraz Document.createNodeWalker()
przy tworzeniu obiektów typu NodeIterator
i TreeWalker
.
Z przekazaniem korzenia (węzeł) i maski bitowej (liczba dziesiętna lub szesnastkowa) nie ma żadnego problemu. Inaczej wygląda sytuacja w przypadku filtra. W zasadzie należy tutaj przekazać obiekt, który będzie zwracał konkretną liczbę (lub odpowiadającą stałą z tabelki), której wartość oznacza akceptację lub odrzucenie danego węzła (z ewentualnym uwzględnieniem lub pominięciem jego potomków). Obiekt ten należy utworzyć samodzielnie przy wykorzystaniu podstawowych poleceń danego środowiska uruchomieniowego.
Zgodnie z zapisem składniowym umieszczonym wyżej można to zrobić na dwa podstawowe sposoby:
- Za pomocą zwykłego obiektu JS z dodatkową metodą o nazwie
"acceptNode"
(nie można przekazać innej nazwy), która będzie wywoływana przy testowaniu węzła. W metodzie tej należy umieścić cały kod testujący, nie zapominając jednocześnie o zwróceniu odpowiedniej wartości. - Za pomocą zwykłej funkcji, która będzie wywoływana przy testowaniu węzła. W funkcji tej należy umieścić cały kod testujący, nie zapominając jednocześnie o zwróceniu odpowiedniej wartości. Nasza funkcja tak naprawdę jest metodą jakiegoś obiektu (globalnego lub kontekstu wykonania). Nazwa samej funkcji lub zmiennej, która na nią wskazuje może być dowolna.
Obydwa sposoby dają dużą swobodę. Do metod tworzących obiekty typu NodeIterator
i TreeWalker
, jako filtr, można bezpośrednio przekazywać obiekty anonimowe, funkcje anonimowe, wyrażenia funkcji jak i deklaracje funkcji. Nie ma w tej kwestii żadnych ograniczeń.
Przy każdym kolejnym przejściu do innego węzła nastąpi automatyczne wywołanie filtra (testu) i w zależności od zwróconego wyniku węzeł jest brany pod uwagę lub pomijany.
Prosty przykład z wykorzystaniem standardowego obiektu JS:
<div id="box">
<p>Pierwszy akapit.</p>
<P>Drugi akapit.</p>
<P>Trzeci akapit.</P>
</div>
<script>
var filter = {
acceptNode: function(node){
// Akceptujemy każdy węzeł
return NodeFilter.FILTER_ACCEPT;
}
};
var root = document.getElementById("box");
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ALL, filter);
var node = iterator.nextNode(); // pierwszy element w obiekcie 'iterator'
document.write("Odczytane węzły w kontenerze DIV za pomocą NodeIterator:" + "<br><br>");
while (node){
document.write(node.nodeName + "<br>");
node = iterator.nextNode();
}
</script>
Prosty przykład z wykorzystaniem funkcji anonimowej i dodatkowych kryteriów filtracji:
<div id="box">
<p>Pierwszy akapit.</p>
<P>Drugi akapit.</p>
<P>Trzeci akapit.</P>
</div>
<script>
var root = document.getElementById("box");
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ALL, function(node){
if (node.nodeName.toLowerCase() == "p"){
return 1;
}
});
var node = iterator.nextNode(); // pierwszy element w obiekcie 'iterator'
document.write("Odczytane węzły w kontenerze DIV za pomocą NodeIterator:" + "<br><br>");
while (node){
document.write(node.nodeName + "<br>");
node = iterator.nextNode();
}
</script>
W powyższym przykładzie wybieramy tylko akapity, dlatego zwróconych zostanie zdecydowanie mniej węzłów (3) niż w przykładzie pierwszym, gdzie akceptowane były dowolne węzły (11).
Na zakończenie przypominam, że ten rodzaj filtracji odbywa się po stronie JS dlatego jest bardzo powolny. Jeśli tylko to możliwe należy stosować predefiniowane metody filtrujące implementowane bezpośrednio w przeglądarce.
Składnia Web IDL#
callback interface NodeFilter { unsigned short acceptNode(Node node); };