javascript - Beste måte å få barneknuter på
Jeg lurte på, JavaScript tilbyr en rekke metoder for å få det første underordnede elementet fra et hvilket som helst element, men hvilken er best? Med best mener jeg: mest kompatibel på tvers av nettlesere, raskest, mest omfattende og forutsigbar når det gjelder oppførsel. En liste over metoder/egenskaper jeg bruker som aliaser:
var elem = document.getElementById('container');
var child = elem.children[0];
var child = elem.firstElementChild; // == children[0]
Dette fungerer for begge tilfeller:
var child = elem.childNodes[0]; // or childNodes[1], see below
Det er i tilfelle av skjemaer, eller <div> iterasjon. Hvis jeg skulle støte på tekstelementer:
var child = elem.childNodes; // treat as NodeList
var child = elem.firstChild;
Så vidt jeg kan forstå, bruker firstChild NodeList fra childNodes, og firstElementChild bruker children. Jeg baserer denne antagelsen på MDN-referansen:
childNodeer en referanse til det første barneelementet til elementnoden, ellernullhvis det ikke finnes et.
Jeg antar at når det gjelder hastighet, vil forskjellen, om det i det hele tatt er noen, være nær null, siden firstElementChild i praksis er en referanse til children[0], og children-objektet allerede er i minnet uansett.
Det som forvirrer meg, er childNodes-objektet. Jeg har brukt det for å ta en titt på et skjema, i et tabellelement. Mens children lister opp alle skjemaelementene, ser det ut til at childNodes også inkluderer hvite mellomrom fra HTML-koden:
console.log(elem.childNodes[0]);
console.log(elem.firstChild);
Begge logger <TextNode textContent="\n ">
console.log(elem.childNodes[1]);
console.log(elem.children[0]);
console.log(elem.firstElementChild);
All logg <input type="text"…>. Hvordan kommer det? Jeg ville forstå at ett objekt ville tillate meg å jobbe med den «rå» HTML-koden, mens det andre holder seg til DOM, men childNodes-elementet ser ut til å fungere på begge nivåer.
For å gå tilbake til mitt opprinnelige spørsmål, vil jeg gjette: hvis jeg vil ha det mest omfattende objektet, er childNodes veien å gå, men på grunn av dets omfattende natur, er det kanskje ikke det mest forutsigbare når det gjelder å returnere elementet jeg ønsker/forventer til enhver tid. Tverrleserstøtte kan også vise seg å være en utfordring i så fall, selv om jeg kan ta feil.
Kan noen klargjøre forskjellen mellom objektene som er tilstede? Hvis det er en hastighetsforskjell, uansett hvor liten, vil jeg gjerne vite det også. Hvis jeg ser dette helt feil, føl deg fri til å opplyse meg.
PS: Vennligst, vær så snill, jeg liker JavaScript, så ja, jeg vil håndtere denne typen ting. Svar som «jQuery ordner dette for deg» er ikke det jeg er ute etter, derfor ingen jQuery-merkelapp.
Svar #1
Høres ut som du overtenker det. Du har lagt merke til forskjellen mellom childNodes og children, som er at childNodes inneholder alle noder, inkludert tekstnoder som består helt av mellomrom, mens children er en samling av bare barneelementene som er elementer. Det er egentlig alt som skal til.
Det er ingenting uforutsigbart med noen av samlingene, selv om det er et par problemer man bør være oppmerksom på:
- IE <= 8 inkluderer ikke tekstnoder som kun består av mellomrom i
childNodes, mens andre nettlesere gjør det - IE <= 8 inkluderer kommentarnoder i
children, mens andre nettlesere kun har elementer
children, firstElementChild og venner er bare bekvemmeligheter, som presenterer et filtrert bilde av DOM begrenset til bare elementer.
Svar #2
firstElementChild er kanskje ikke tilgjengelig i IE<9 (kun firstChild)
på IE<9 er firstChild det samme som firstElementChild fordi MS DOM (IE<9) ikke lagrer tomme tekstnoder. Men hvis du gjør det i andre nettlesere, vil de returnere tomme tekstnoder...
min løsning
child=(elem.firstElementChild||elem.firstChild)
dette vil gi firstchild selv på IE<9
Svar #3
Den kryssleserkompatible måten å gjøre dette på er å bruke childNodes for å få NodeList, deretter lage et array av alle noder med nodeType ELEMENT_NODE.
/**
* Return direct children elements.
*
* @param {HTMLElement}
* @return {Array}
*/
function elementChildren (element) {
var childNodes = element.childNodes,
children = [],
i = childNodes.length;
while (i--) {
if (childNodes[i].nodeType == 1) {
children.unshift(childNodes[i]);
}
}
return children;
}
http://jsfiddle.net/s4kxnahu/
Dette er spesielt enkelt hvis du bruker et verktøybibliotek som lodash:
/**
* Return direct children elements.
*
* @param {HTMLElement}
* @return {Array}
*/
function elementChildren (element) {
return _.where(element.childNodes, {nodeType: 1});
}
Fremtid:
Du kan bruke querySelectorAll i kombinasjon med :scope pseudo-klasse (matcher elementet som er referansepunktet for selektoren):
parentElement.querySelectorAll(':scope > *');
På tidspunktet for skrivingen støttes :scope i Chrome, Firefox og Safari.
