Новинки DOM API

в 19:37, , рубрики: dom, IT-стандарты, javascript, Веб-разработка, метки: ,

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

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 — перед и после текущей ноды, соответственно.

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

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

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

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

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

DOM4 Mutation methods реализованы в последней версии 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])

Подержку 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 реализованы во всех современных браузерах (уже больше года). Polyfill для старых браузеров.

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

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

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

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

label.control.value = "123"

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

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

console.log(input.labels.length)

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

HTMLOlElement.prototype.revert

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

Свойство revent у нумерованного списка, заставляет нумерацию идти в обратную сторону. При это учитывается свойство 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

Поддержка свойства revert есть в 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 или напишите мне в личку. Также напишите в комментариях, если какаю-то тему нужно рассмотреть подробнее или нужно больше примеров.

Автор: termi

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js