Pull to refresh

Новинки DOM API

Reading time6 min
Views16K
В данной статье я расскажу о новинках в DOM API, которые мы можем использовать уже сейчас или в ближайшем будущем.
Публикация статьи приурочена к радостному событию начала реализации некоторых новых DOM4 API методов в Google Chrome. Многие методы и свойства можно использовать уже сейчас, некоторые из них работают через префиксы, но к каждому методу или свойству я постараюсь дать Polyfill, реализующий их или отбрасывающий браузерные префиксы.
Методы я постарался описать в соответствии с JSDoc для Google Closure Compiler.


DOM4 Mutation methods


Спецификация

У Element.prototype появился ряд очень интересных методов, которые будут знакомы любителям jQuery, однако работают несколько по-другому.

  • append(...{(Node|string)})
  • prepend(...{(Node|string)})
  • before(...{(Node|string)})
  • after(...{(Node|string)})

    Данные метод вставляет в текущую ноду n-нное количество нод. Ноды передаются как аргументы функции (Например: node.append(otherNode1, otherNode2).
    При это, вы можете передать вместо ноды текст, и он автоматически преобразуется в TextNode, как если бы вы вызвали document.createTextNode(text).
    Функция append вставляет ноды в конец своего списка нод, prepend — в начало, функции before и after — перед и после текущей ноды, соответственно.

  • remove()
    Метод удаляет текущую ноду из родителя.
  • replace(...{(Node|string)})
    Метод заменяет текущую ноду, одной или несколькими нодами, которые указываются в качестве параметров метода.

    Все эти методы не имеют возвращаемого значения.


Вот такой паттерн позволит вам передавать в этот метод NodeList или массив с нодами:

element.append.apply(element, document.querySelectorAll("div"))


В document было добавлено два метода из этих шести: append и prepend.

Из всех DOM4 Mutation methods только remove реализован в последней версии Google Chrome.
Polyfill этих методов для всех браузеров есть в моей библиотеке.

DOM Selector API 2: NodeRef и :scope


Спецификация

  • document.querySelector(string, NodeRef{(Node|NodeList|Array.<Node>)})
  • document.querySelectorAll(string, NodeRef{(Node|NodeList|Array.<Node>)})
  • document.find(string, NodeRef{(Node|NodeList|Array.<Node>)})
  • document.findAll(string, NodeRef{(Node|NodeList|Array.<Node>)})


Старые добрые методы querySelector[All] обзавелись вторым параметром, но только в реализации для document. А также добавились новые методы find[All].

Второй параметр позволяет указать контекст, в котором мы будет искать ноды по селектору. Например:

document.findAll("li", [ulElement1, ulElement2])


Найдёт все элементы <li> в двух элементах ulElement1 и ulElement2.

Псевдо класс :scope позволяет делать действительно класные вещи. Это ещё один способ указать контекст поиска — он указывает на текущий элемент, в котором мы проводим поиск по селектору.
Он позволяет искать по ранее не валидным селекторам ">.class" или "~tagname". Просто укажите :scope вначале и данные селекторы станут валидными. Вся мощь :scope видна, когда
мы применяем его вместе с NodeRef:

document.querySelectorAll(":scope > p", [divElement1, divElement2])

Найдёт всех непосредственных детей с тэгом p у divElement1 и divElement2! Хочу заметить, что вы можете не указывать псевдо-класс :scope для методов find и findAll. Для этих методов такой вызов считается валидным:
document.findAll("> p", [divElement1, divElement2])


В данный момент WebKit поддерживает псевдо-класс :scope, но он делает это неправильно. Хорошая новость в том, что после моего баг-репорта, неправильную поддержку :scope уберут. Если вам всё-таки потребуется проверить настоящую поддержку данного псевдо-класса, вот код для этого: gist.
Подержку find[All] и :scope в querySelectorAll, соответствующую спецификации, я сейчас делаю.

Element.prototype.matches


Спецификация

Этот метод ранее назывался matchesSelector. Он проверяет ноду на соответствие CSS-селектору.
Маленький polyfill с отбрасывание браузерных префиксов: gist. Более расширенный вариант у меня в библиотеке.

classList


Спецификация

DOM API для работы с CSS-классами элемента.

Методы:

  • add(...class{string}) Добавляет class в element.className
  • remove(...class{string}) Удаляет class из element.className
  • toggle(boolean: class{string}) Удаляет class в случии его наличия из element.className и возвращает false. Добавляет в случае его отсутсвие в element.className и возвращает true.
  • contains(boolean: class{string}) Проверяет class на его наличие в element.className. Возвращает true или false соответственно.
Ранее, методы add и remove работали только с одним классом за раз, а недавно в стандарт была добавлена возможность работать с несколькими CSS-классами:

document.documentElement.classList.add("test1", "test2", "test3")
document.documentElement.classList.remove("test1", "test2", "test3")


Polyfill для classList, пока старой спецификации, есть у меня в библиотеке. В скором времени будет доступна и новая версия, которая будет патчить старую реализацию.

Events Constructor


Спецификация

В новом стандарте DOM API определены конструкторы для событий. Теперь мы можем забыть про мучения с

event = document.createEvent("click")
event.initMouseEvent( /*тут что-то, я даже сам не помню что*/ )


А просто написать:

event = new Event("click")


В конструктор мы можем передать любое текстовое значение в качестве e.type, вторым параметром передаётся объект, который содержит инициализационные параметры bubbles и cancelable. bubbles установленное в false предотвратит всплытие события. cancelable установленное в false предотвратит отмену события через метод preventDefault.
По-умолчанию, bubbles и cancelable равны false.

event = new Event("some:event", {bubbles : true, cancelable : false})
event = new Event("dbclick1")
element.dispachEvent(event)


Если необходимо добавить данные для обработчика, надо просто добавить их в объект event:

event = new Event("click")
event.button = 1


Замечу, что при этом все созданные таким способом события являются обычными событиями, т.е. new Event("click") создаст не MouseEvent, а просто Event.

Второй класс для создания событий — это CustomEvent. Использование этого класса отличается только тем, что в инициализационный объект можно передать свойство detail

event = new CustomEvent("somecustom:event", {bubbles : true, cancelable : false, detail : {"some data" : 123}})


Конструкторы Event и CustomEvent реализованы во всех современных браузерах (уже больше года) кроме Android WebKit браузеров. Polyfill для старых браузеров.

Также, идёт обсуждение, для специальных событий клавиатуры, Drag&Drop и мыши. Но пока они не реализованы ни одним браузером.
(Замечание: Opera 12.10 поддерживает конструктор для KeyboardEvent, но он работает не по спецификации)

HTMLLabelElement.prototype.control и HTML*Element.prototype.labels для элементов формы


Спецификация для control

Свойство control содержит ссылку на элемент формы с которым связан данный <label> элемент:

label.control.value = "123"

Напомню, что по спецификации, у одного элемента <label> может быть только один связанный элемент формы.

Спецификация для labels

Свойство labels, наоборот, содержит NodeList элементов <label> для элемента формы. NodeList потому, что по спецификации у одного элемента формы может быть несколько связанных <label> элементов.

console.log(input.labels.length)


Свойства реализованы в большинстве браузеров. Polyfill для старых браузеров

HTMLOlElement.prototype.reversed


Спецификация

Свойство reversed у нумерованного списка, заставляет нумерацию идти в обратную сторону. При это учитывается свойство start. Вот простые примеры, которые должны сказать больше слов:

<ol reversed>  
    <li>Элемент списка 1</li>
    <li>Элемент списка 2</li>  
    <li>Элемент списка 3</li>
    <li>Элемент списка 4</li>  
    <li>Элемент списка 5</li>  
</ol>


Будет интерпретировано браузером как:

5. Элемент списка 1
4. Элемент списка 2
3. Элемент списка 3
2. Элемент списка 4
1. Элемент списка 5


А такая разметка:

<ol reversed start=100>  
    <li>Элемент списка 1</li>  
    <li>Элемент списка 2</li>  
    <li>Элемент списка 3</li>  
    <li>Элемент списка 4</li>  
    <li>Элемент списка 5</li>  
</ol>


будет интерпретирована браузером как:

100. Элемент списка 1
99. Элемент списка 2
98. Элемент списка 3
97. Элемент списка 4
96. Элемент списка 5


Поддержка свойства reversed есть в Google Chrome. Для остальных браузеров есть Polyfill.

Event.prototype.stopImmediatePropagation


Спецификация

Метод работает аналогично такому же методу из jQuery.

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

Только Opera до версии 12.10 не поддерживает этот метод. Polyfill для неё.

Поддержка IE < 9



В IE8 есть прототипы DOM-объектов, поэтому добавить туда поддержку не составит труда. Я вынес polyfill'ы для IE8 в отдельный файл и подключаю его через Conditional Comments.

Для IE6/7 всё сложнее, нужно либо отказаться от этих браузеров полностью или полностью реализовывать DOM API для них, что я и сделал, о чём рассказывал в своей статье DOM-shim для всех браузеров включая IE < 8.

Код статьи выложен на github. Если вы хотите помочь или нашли ошибку, сделайте pull request или напишите мне в личку. Также напишите в комментариях, если какую-то тему нужно рассмотреть подробнее или нужно больше примеров.
Tags:
Hubs:
+52
Comments48

Articles