Статьи

Сравнение позиции документа

Большой пост в блоге , для меня, был написан один ППК спине около двух лет примерно , в котором он объяснил , как contains()и compareDocumentPosition()методы работы в своих браузерах. С тех пор я провел много исследований этих методов и использовал их несколько раз. Как выяснилось, они невероятно полезны для ряда задач (особенно связанных с конструированием селекторных движков с чистой DOM).

DOMElement.contains (DOMNode)

Первоначально представленный Internet Explorer, этот метод определяет, содержится ли один узел DOM в другом элементе DOM. Этот метод может быть особенно полезен при попытке оптимизировать обходы CSS Selector, которые выглядят как «# id1 # id2». С помощью этого метода вы можете использовать getElementByIdоба элемента, .contains()чтобы определить, что # id1 действительно содержит # id2.

Есть одно замечание: .contains()вернет true, если узел DOM и элемент DOM идентичны (хотя технически элемент не может содержать себя).

Вот простая реализация реализации, которая работает в Internet Explorer, Firefox, Opera и Safari.

функция содержит
( a, b
)
{

 
return a.
содержит ?

    a! = b && a.
содержит
( б
) :

    !!
( a.
CompareDocumentPosition
( arg
) &
16
) ;

}

Обратите внимание, что мы используем compareDocumentPosition, что мы будем обсуждать дальше.

DOMNode.compareDocumentPosition (DOMNode)

Этот метод является частью спецификации DOM Level 3 и позволяет определить, где находятся два узла DOM по отношению друг к другу. Этот метод гораздо более мощный .contains(). Одним из возможных применений этого метода является изменение порядка узлов DOM в определенном порядке (как это было сделано в PPK).

С помощью этого метода вы можете определить целый ряд информации, относящейся к положению элемента. Вся эта информация возвращается с использованием битовой маски.

Для тех, кто не знаком с этим, битовая маска — это способ хранения нескольких точек данных в одном номере. Вы заканчиваете тем, что включаете / выключаете отдельные биты числа, давая вам окончательный результат.

Вот результаты NodeA.compareDocumentPosition(NodeB)вместе со всей информацией, к которой вы можете получить доступ:

Биты номер Смысл
000000 0 Элементы идентичны.
000001 1 Узлы находятся в разных документах (или один находится вне документа).
000010 2 Узел B предшествует Узлу A.
000100 4 Узел А предшествует Узлу Б.
001000 8 Узел B содержит Узел A.
010000 16 Узел А содержит Узел Б.
100000 32 Для частного использования браузером.

Теперь это означает, что возможным результатом может быть что-то вроде:

<div id = «a» >
<div id = «b» >
</ div>
</ div>

<script>

alert (document.getElementById («a»)

  .compareDocumentPosition (document.getElementById («b»)) = = 20);

</ скрипт>

Поскольку узел, который содержит другой, как «содержит» его (+16), так и предшествует ему (+4), конечным результатом является число 20. Это может иметь больше смысла, если вы посмотрите на то, что происходит с битами:

000100 (4) + 010000 (16) = 010100 (20)

Это, несомненно, делает для единственного наиболее запутанного метода DOM API — однако это тот, чья ценность будет вполне заслужена.

Прямо сейчас DOMNode.compareDocumentPositionдоступно в Firefox и Opera. Тем не менее, есть некоторые приемы, которые мы можем использовать для полной реализации в Internet Explorer:

// Сравнить позицию — MIT Licensed, John
Resig

function comparePosition
( a, b
)
{

 
return a.
CompareDocumentPosition ?

    а.
CompareDocumentPosition
( b
) :

    a.
содержит ?

     
( a! = b && a.
содержит
( b
) &&
16
) +

       
( a! = b && b.
содержит
( a
) &&
8
) +

       
( a.
sourceIndex > =
0 && b.
sourceIndex > =
0 ?

         
( a.
sourceIndex <b.
sourceIndex &&
4
) +

           
( a.
sourceIndex > b.
sourceIndex &&
2
) :

         
1
) +

     
0 :

     
0 ;

}

Internet Explorer предоставляет нам несколько методов и свойств, которые мы можем использовать. Начнем с .contains()метода (как мы уже обсуждали ранее), который дает нам содержит (+16) и «содержится» (+8). Internet Explorer также имеет .sourceIndexсвойство для всех элементов DOM, соответствующее положению элемента абсолютно в документе. Например, document.documentElement.sourceIndex == 0. Поскольку у нас есть эта информация, мы можем завершить еще две части compareDocumentPositionголоволомки: перед (+2) и затем (+4). Кроме того, если элемент в настоящее время не находится в документе, он .sourceIndexбудет равен -1, что дает нам ответ 1. Наконец, с помощью процесса дедукции мы можем определить, равен ли элемент самому себе, возвращая пустую битовую маску 0.

Эта функция будет работать в Internet Explorer, Firefox и Opera. У нас будет только ограниченная функциональность в Safari (поскольку он имеет только .contains(), и нет .sourceIndex, мы получим только «содержит» +8 и «содержится» +16 — все остальные результаты будут возвращать «1», представляющий разъединение).

PPK представляет собой прекрасный пример того, как эта новая функциональность может быть использована при создании getElementsByTagNamesметода. Давайте адаптируем его для работы с нашим новым методом:

// Оригинал от PPK quirksmode.org

Функция getElementsByTagNames
( list, elem
)
{

        elem = elem || документ;

       

       
var tagNames = список.
split
(
‘,’
) , results =
[
] ;

       

       
for
(
var i =
0 ; i <tagNames.
length ; i ++
)
{

               
var tags = elem.
getElementsByTagName
( tagNames
[ i
]
) ;

               
для
(
варj =
0 ; j <теги.
длина ; j ++
)

                        результаты.
push
( теги
[ j
]
) ;

       
}

       

       
вернуть результаты.
sort
(
function
( a, b
)
{

               
return
3
( comparePosition
( a, b
) &
6
) ;

       
}
) ;

}

Теперь мы можем использовать это для построения по порядку оглавления для сайта:


getElementsByTagname
(
«h1, h2, h3»
) ;

Хотя и Firefox, и Opera предприняли некоторую инициативу для реализации этого метода, я с нетерпением жду появления большего количества браузеров, которые помогут продвинуть это вперед.


Примечание: в jQuery вы можете сделать $ (» : header «), чтобы выбрать все элементы заголовка по порядку.