본문 바로가기

Development

Selectors API Level 2

http://www.w3.org/TR/selectors-api2/

아래와 같은 문서가 있을 때
<table id="score"> <thead> <tr> <th>Test <th>Result <tfoot> <tr> <th>Average <td>82% <tbody> <tr> <td>A <td>87% <tr> <td>B <td>78% <tr> <td>C <td>81% </table>
기존에는 다음과 같은 스크립트를 통해 각 엘리먼트를 조작했다.
var table = document.getElementById("score"); var groups = table.tBodies; var rows = null; var cells = []; for (var i = 0; i < groups.length; i++) { rows = groups[i].rows; for (var j = 0; j < rows.length; j++) { cells.push(rows[j].cells[1]); } }
새로운 API 를 사용하게 되면 아래와 같다.
var cells = document.querySelectorAll("#score>tbody>tr>td:nth-of-type(2)");

이에 사용되는 인터페이스를 보자
interface NodeSelector {
    Element   querySelector(in DOMString selectors, in optional any refNodes);
    NodeList  querySelectorAll(in DOMString selectors, in optional any refNodes);
    Element   queryScopedSelector(in DOMString selectors);
    NodeList  queryScopedSelectorAll(in DOMString selectors);
  };
각 노드를 선택하는 API 들이다.
아래는 매치셀렉터이다.
interface Element {
    boolean   matchesSelector(in DOMString selectors, in optional any refNodes]);
  };

이어지는 예제는 아래와 같다.

The following examples make use of this sample XHTML document.

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Selectors API Example</title>
  </head>
  <body>
    <div id="foo">
      <p class="warning">This is a sample warning</p>
      <p class="error">This is a sample error</p>
    </div>
    <div id="bar">
      <p>...</p>
    </div>
  </body>
</html>

The methods accept a group of selectors (comma separated) as the argument. The following example would select all p elements in the document that have a class of either "error" or "warning".

var alerts = document.querySelectorAll("p.warning, p.error");

The querySelector() methods also accept a group of selectors and they will return the first element (if any) that matches any of the selectors in the group.

var x = document.querySelector("#foo, #bar");

x would contain the first element in the document with an ID of either foo or bar, or null if there is no such element. In the sample document above, it would select the div element with the ID of foo because it is first in document order. The order of the selectors used in the parameter has no effect and would have the same result if the order were reversed, as in:

var x = document.querySelector("#bar, #foo");

The methods can also be invoked on elements. In the following example, assume the event handler is registered on an element, and thus the method is invoked on the target element of the event.

function handle(evt) {
  var x = evt.target.querySelector("span");
  ...
  // Do something with x
}

Even though the method is invoked on an element, selectors are still evaluated in the context of the entire document. In the following example, the method will still match the divelement's child p element, even though the body element is not a descendant of the div element itself.

var div = document.getElementById("bar");
var p = div.querySelector("body p");

Given this sample fragment that contains a list as a navigation menu:

<ul class="nav">
  <li><a href="/">Home</a></li>
  <li><a href="/products">Products</a></li>
  <li><a href="/about">About</a></li>
</ul>

The following example selects all the li elements and demonstrates how to iterate through the collection in a NodeList.

var lis = document.querySelectorAll("ul.nav>li");
for (var i = 0; i < lis.length; i++) {
  process(lis.item(i));
}

In ECMAScript, the language binding also allows NodeLists to be addressed using the array notation, so that loop could be rewritten like this:

for (var i = 0; i < lis.length; i++) {
  process(lis[i]);
}

Since the NodeList objects returned by these methods are not live, changes to the DOM do not affect the content of the list. Consider the process() function called in the previous examples is defined as follows:

function process(elmt) {
  elmt.parentNode.removeChild(elmt);
}

This would cause each selected element to be removed from the DOM, but each element will remain in the NodeList. If the list were a live NodeList, removing an item from the DOM would also remove the element from the list and adjust the indexes of subsequent elements. That would have adverse effects upon the loop because not all selected elements would be processed.

In documents comprising elements from multiple namespaces, it's possible that some elements from different namespaces share the same local name. Since this API does not natively support a namespace resolution mechanism for selectors, obtaining a list of such elements from a specific namespace, excluding all others, requires additional processing to filter the result. The following example illustrates a document containing video elements from both the SVG and XHTML namespaces.

<svg id="svg1" xmlns="http://www.w3.org/2000/svg"
               xmlns:xlink="http://www.w3.org/1999/xlink">
  <video id="svgvideo1" xlink:href="myvideo.ogg" width="320" height="240"/>
  <foreignObject width="100" height="100">
    <video id="htmlvideo1" src="myvideo.ogg" xmlns="http://www.w3.org/1999/xhtml">No video1</video>
  </foreignObject>
</svg>

The following script demonstrates how to first select the video elements and then filter out the unwanted elements based on their namespace.

var list = document.querySelectorAll("svg video");
var result = new Array();
var svgns = "http://www.w3.org/2000/svg"

for(var i = 0; i < elms.length; i++) {
  if(list[i].namespaceURI == svgns) {
    result.push(elms[i]);
  }
}