Technische Umsetzung von contains() für Element in JavaScript
Michael_K
- javascript
Hallo,
gibt es irgendwo eine Erklärung, wie die DOM Methode contains() technisch umgesetzt wird? Mit dieser Methode kann geprüft werden, ob ein Element ein Unterelement von einem anderen Element ist:
const parent = document.querySelector("div");
const child = document.querySelector("p");
// Test ob erstes p-Element im ersten div-Element enthalten ist
console.log(parent.contains(child));
Mich interessiert, wie dies technisch umgesetzt wird, d.h. wie erfolgt die funktionale Ermittlung. Hat im DOM jeder Knoten einen versteckten Identifier? Oder wie erfolgt die Ermittlung.
Gruss, Michael
Hallo Michael_K,
jeder Knoten im DOM kennt seinen Elternknoten. Für a.contains(b) muss man von b aus die Elternkette durchlaufen und stößt entweder auf a oder den Wurzelknoten.
Rolf
Hallo Rolf,
vielen Dank. Mit dieser Ermittlungsmethode würde sich die Folgefrage stellen, auf welcher technischen Basis abgeglichen wird, ob bei zwei Elementen es sich um das Gleiche handelt:
b.parentElement === a
Das kann doch de facto nur über einen versteckten Identifier/Index funktionieren?
Gruss
Hallo Michael_K,
das DOM enthält Objekte. Der Vergleich prüft, ob es das gleiche Objekt ist. Dazu kann man die Adresse verwenden, an der das Objekt im Speicher liegt. Diese Adresse ist für das JavaScript Programm nicht verfügbar, für die JS Engine aber schon.
Rolf
Danke, ist interessant. Bei jsdom gibt es eine Methode jsdom.nodeLocation(element), die de facto einen Positionierungsindex bereitstellt, um diesen Abgleich durchzuführen.
Lieber Michael_K,
Bei jsdom gibt es eine Methode jsdom.nodeLocation(element), die de facto einen Positionierungsindex bereitstellt, um diesen Abgleich durchzuführen.
in JavaScript kann man Objekte mit dem einfachen Vergleichsoperator (das doppelte Istgleichzeichen) vergleichen, da dieser einfache Vergleich immer scheitert, wenn es sich nicht um das identische Objekt (also die Instanz, nicht die Art oder Klasse) handelt.
const a = new Object();
const b = new Object();
const c = a;
c.neueEigenschaft = 1;
if (a == b) {
// dieser Fall tritt nicht ein:
console.log("a und b ist das identische Objekt.");
} else {
console.log("a und b sind unterschiedliche Objekte.");
}
if (a == c) {
console.log("a und c ist das identische Objekt.");
} else {
// dieser Fall tritt nicht ein:
console.log("a und c sind unterschiedliche Objekte.");
}
if (b == c) {
// dieser Fall tritt nicht ein:
console.log("b und c ist das identische Objekt.");
} else {
console.log("b und c sind unterschiedliche Objekte.");
}
Ja, intern wird die JavaScript-Engine sicherlich irgendwelche Identifier oder Speicheradressen dafür verwenden, aber für Dich als Programmierer ist das völlig irrelevant. Und DOM-Knoten sind auch „nur“ Objekte.
Liebe Grüße
Felix Riesterer
Hallo Felix,
mir ist bewusst, dass die Vergleichsoperatoren auch für Element-Knoten angewendet werden können. Mich interessiert aber, auf welcher technischen Basis dieser Vergleich durchgeführt wird.
Rolf hat in seinem Beitrag ausgeführt, dass hierzu diese Speicheradresse genutzt wird, in der die JS Engine das Objekt ablegt. Eine weitere mögliche Variante wäre, es mit Symbol() umzusetzen, und somit jedem Element-Knoten einen eindeutigen Index anzuhängen (wenn der Dom sich ändert, müsste aber ggfs. angepasst werden).
aber für Dich als Programmierer ist das völlig irrelevant
Nein, ist es nicht. Wenn der DOM nicht zur Verfügung steht und die geparsten Elemente in unterschiedlichen Objekten hängen, muss man es selbst effizient umsetzen.
Gruss
Hallo Michael_K,
Was ist dein Anwendungsfall?? Kein DOM hieße kein Browser…
Wenn du das in node.js machen willst, gibt es dort sicherlich Libraries, die ein DOM nachbilden oder das html in einen Objektbaum parsen können.
Rolf
Was ist dein Anwendungsfall?? Kein DOM hieße kein Browser…
Web-Worker (wieder einmal ;-), da steht kein DOM zur Verfügung, da nicht thread-safe.
Es werden mehrere große Third-Party XML Dateien via Sax Parser (konkret saxjs) eingelesen. JSDOM ist keine Alternative, da dass mächtig in den Speicher gehen würde. Um das Ganze schlank zu halten, soll nur ein Minimum an Daten beim parsen (via stream) im Speicher gehalten werden, die bei gewissen XML-Fragmenten eingelsen werden.
Lieber Michael_K,
Web-Worker (wieder einmal ;-), da steht kein DOM zur Verfügung, da nicht thread-safe.
warum steht das nicht im OP? Das wäre die zentrale Information gewesen, um abschätzen zu können, wie Dir zu helfen ist.
XML Dateien via Sax Parser (konkret saxjs) eingelesen. JSDOM ist keine Alternative, da dass mächtig in den Speicher gehen würde.
Und was ist mit XSDOMParser? Oder xmldom?
Liebe Grüße
Felix Riesterer
Hallo Felix
warum steht das nicht im OP? Das wäre die zentrale Information gewesen, um abschätzen zu können, wie Dir zu helfen ist.
Weil es für meine Frage nicht relevant war. Ich möchte verstehen, wie es technisch gelöst wird, so dass ich meine Alternativen beurteilen kann.
Und was ist mit XSDOMParser? Oder xmldom?
Sind keine stream-basierten Parser.
Gruss Michael
Hallo Michael_K,
Weil es für meine Frage nicht relevant war.
Na und ob. Ohne DOM keine Elternkette und damit ist der Algorithmus für's DOM obsolet.
SAX Parser arbeiten nicht mit dem vollständigen Dokument, sie werfen Events pro Node, den sie antreffen. Und sie haben kein Gedächtnis. Wenn du das opentag Event für <foo> bekommst und wissen musst, ob es <bar> als Elternelement hat, dann musst du ein eigenes Gedächtnis bauen. Z.B. ein Stack der offenen Elemente, den du dann durchsuchen kannst. Beim opentag legst du was drauf, beim closetag nimmst du das Element runter. SAXJS sollte sowas auch für den Eigengebrauch haben, aber beim schnellen Blick auf die github Seite finde ich keine Doku dazu. Das wäre ein Forschungsthema.
Rolf
Hallo Rolf,
ich nutze saxjs schon eine ganze Weile, funktioniert sehr gut und ist beeindruckend, mit wie wenig Ressourcen man die großen Dateien auslesen kann.
So wie du es beschreibst, mit einem Stack, gehe ich auch vor.
dann musst du ein eigenes Gedächtnis bauen.
Und um hier nicht das Rad neu zu erfinden, wollte ich wissen, wie es JavaScript bei DOM löst. Ich werden nun einen einfachen Index verwenden, damit eine Abfrage möglich ist.
Gruss Michael