Книга: JavaScript. Подробное руководство, 6-е издание

15.3.2. Документы как деревья элементов

15.3.2. Документы как деревья элементов

Когда основной интерес представляют сами элементы документа, а не текст в них (и пробельные символы между ними), гораздо удобнее использовать прикладной интерфейс, позволяющий интерпретировать документ как дерево объектов Element, игнорируя узлы Text и Comment, которые также являются частью документа.

Первой частью этого прикладного интерфейса является свойство children объектов Element. Подобно свойству childNodes, его значением является объект NodeList. Однако, в отличие от свойства childNodes, список children содержит только объекты Element. Свойство children - нестандартное свойство, но оно реализовано во всех текущих броузерах. В IE это свойство было реализовано уже очень давно, и большинство других броузеров последовали его примеру. Последним основным броузером, реализовавшим его, стал Firefox 3.5.

Обратите внимание, что узлы Text и Comment не имеют дочерних узлов. Это означает, что описанное выше свойство Node.parentNode никогда не возвращает узлы типа Text или Comment. Значением свойства parentNode любого объекта Element всегда будет другой объект Element или корень дерева - объект Document или DocumentFragment.

Второй частью прикладного интерфейса навигации по элементам документа являются свойства объекта Element, аналогичные свойствам доступа к дочерним и братским узлам объекта Node:

firstElementChild, lastElementChild

Похожи на свойства firstChild и lastChild, но возвращают дочерние элементы.

nextElementSibling, previousElementSibling

Похожи на свойства nextSibling и previousSibling, но возвращают братские элементы.

childElementCount

Количество дочерних элементов. Возвращает то же значение, что и свойство children.length.

Эти свойства доступа к дочерним и братским элементам стандартизованы и реализованы во всех текущих броузерах, кроме IE.[37]

Поскольку прикладной интерфейс навигации по элементам документа реализован не во всех броузерах, вам может потребоваться определить переносимые функции навигации, как в примере 15.2.

Пример 15.2. Переносимые функции навигации по документу

/**
* Возвращает ссылку на n-го предка элемента е или null, если нет такого предка
* или если этот предок не является элементом Element
* (например, Document или DocumentFragment).
* Если в аргументе n передать 0, функция вернет сам элемент е.
* Если в аргументе n передать 1 (или вообще опустить этот аргумент),
* функция вернет родительский элемент.
* Если в аргументе n передать 2, функция вернет родителя родительского элемента и т. д. */
function parent(e, n) {
  if (n === undefined) n = 1;
  while(n-- && e) e = e.parentNode;
  if (!e || e.nodeType !== 1) return null;
  return e;
}
/**
* Возвращает n-й братский элемент элемента е.
* Если в аргументе п передать положительное число, функция вернет следующий
* n-й братский элемент.
* Если в аргументе п передать отрицательное число, функция вернет предыдущий
* n-й братский элемент.
* Если в аргументе п передать ноль, функция вернет сам элемент е.
*/
function sibling(e.n) {
  while(e && n !== 0) { // Если e не определен, просто вернуть его
    if (n > 0) { // Отыскать следующий братский элемент
      if (е.nextElementSibling) е = е.nextElementSibling;
      else {
        for(e=e.nextSibling; e && e.nodeType !== 1; e=e.nextSibling)
            /* пустой цикл */ ;
      }
      n--;
    }
    else { // Отыскать предыдущий братский элемент
      if (e.previousElementSibing) e = e.previousElementSibling;
      else {
        for(e=e.previousSibling;e&&e.nodeType!==1;e=e.previousSibling)
              /* пустой цикл */ ;
      }
    n++;
    }
  }
  return e;
}
/**
* Возвращает n-й дочерний элемент элемента е или null, если нет такого
* дочернего элемента.
* Если в аргументе n передать отрицательное число, поиск дочернего элемента
* будет выполняться с конца. 0 соответствует первому дочернему элементу,
* но -1 - последнему, -2 - второму с конца и т. д.
*/
function child(e, n) {
  if (e.children) { // Если массив children существует
    if (n < 0) n += e.children.length; // Преобразовать отрицательное
                                       // число в индекс массива
    if (п < 0) return null; // Если получилось отрицательное число,
                  // значит, нет такого дочернего элемента
    return е.children[n3; // Вернуть заданный дочерний элемент
  }
  // Если элемент е не имеет массива children, начать поиск с первого
  // дочернего элемента, двигаясь вперед, или начать поиск с последнего
  // дочернего элемента, двигаясь назад.
  if (n >= 0) { // n - положительное: двигаться вперед, начиная с первого
    // Найти первый дочерний элемент элемента е
    if (е.firstElementChild) е = е.firstElementChild;
    else {
      for(e = е.firstChild; е && e.nodeType !== 1; е = е.nextSibling)
            /* пустой цикл */;
    }
    return sibling(e, n); // Вернуть n-го брата первого дочернего элемента
  }
  else { // n - отрицательное: двигаться назад, начиная с последнего
    if (е.lastElementChild) е = е.lastElementChild;
    else {
      for(e = e.lastChild; e && e.nodeType !== 1; e=e.previousSibling)
           /* пустой цикл */;
    }
    return sibling(e, n+1); // +1, чтобы преобразовать номер -1 дочернего
                            // в номер 0 братского для последнего
  }
}

Определение собственных методов элементов

Все текущие броузеры (включая ІE8 и выше) реализуют модель DOM таким образом, что такие типы, как Element и HTMLDocument[38], являются классами, такими же как классы String и Array. Они не имеют конструкторов (как создавать новые объекты Element, будет показано далее в этой главе), но они имеют объекты-прототипы, которые вы можете расширять своими методами:

Element.prototype.next = function() {
      if (this.nextElementSibling) return this.nextElementSibling;
      var sib = this.nextSibling;
      while(sib && sib.nodeType !== 1) sib = sib.nextSibling;
      return sib;
};

Функции, представленные в примере 15.2, не были реализованы в виде методов объекта Element лишь по той причине, что такая возможность не поддерживается в ІE7.

Однако возможность расширения типов DOM может пригодиться для реализации особенностей, характерных для IE, в других броузерах. Как отмечалось выше, нестандартное свойство children объекта Element было впервые реализовано в IE и только потом - в других броузерах. Используя следующий программный код, можно реализовать это свойство в броузерах, не поддерживающих его, таких как Firefox 3.0:

// Реализация свойства Element.children в броузерах,
// не поддерживающих его
// Обратите внимание, что этот метод возвращает статический
// массив, а не "живой" NodeList
if (!document.documentElement.children) {
  Element.prototype.__defineGetter__("children",
     function() { var kids = [];
       for(var c = this.firstChild; c != null; c = c.nextSibling)
         if (c.nodeType === 1) kids.push(c);
       return kids;
     }):
}

Метод __defineGetter__ (о нем рассказывается в разделе 6.7.1) не является

стандартным, но его вполне можно использовать для обеспечения переносимости в таком программном коде, как этот.

Оглавление книги


Генерация: 1.329. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз