• Главная
  • Карта сайта
Не найдено

Основы манипуляции с DOM в ванильном JavaScript (без jQuery)

  1. DOM Manipulation: запрос к DOM
  2. Работа с ноделистами
  3. Модификация Классов и Атрибутов
  4. Добавление стилей CSS
  5. Модификация DOM
  6. Свойства элемента
  7. Прослушивание событий
  8. Предотвращение действий по умолчанию
  9. Делегация мероприятия
  10. Анимация
  11. Написание собственных вспомогательных методов
  12. демонстрация
  13. Заключение

Эта статья включена в нашу антологию, Современный JavaScript , Если вы хотите, чтобы все в одном месте было в курсе современного JavaScript, зарегистрируйтесь в SitePoint Premium и загрузите себе копию.

Современный JavaScript

Всякий раз, когда нам нужно выполнить DOM-манипуляции, мы все быстро достигаем jQuery. Тем не менее, ванильный JavaScrpt DOM API на самом деле вполне способен, и, поскольку IE <11 был официально заброшенный Теперь он может быть использован без каких-либо забот.

В этой статье я покажу, как выполнить некоторые из наиболее распространенных задач манипулирования DOM с помощью простого JavaScript, а именно:

  • запрашивать и модифицировать DOM,
  • изменение классов и атрибутов,
  • прослушивание событий и
  • анимация.

В заключение я покажу вам, как создать свою собственную сверхтонкую DOM-библиотеку, которую вы можете добавить в любой проект. Попутно вы узнаете, что манипулирование DOM с помощью vanilla JS - это не ракетостроение, и что многие методы jQuery на самом деле имеют прямые эквиваленты в нативном API.

Итак, давайте вернемся к этому ...

DOM Manipulation: запрос к DOM

Обратите внимание: я не буду подробно описывать API-интерфейс Vanilla DOM, а лишь коснусь его поверхности. В примерах использования вы можете встретить методы, которые я не представил явно. В этом случае просто обратитесь к отличному Сеть разработчиков Mozilla для деталей.

DOM может быть запрошен с использованием метода .querySelector (), который принимает произвольный селектор CSS в качестве аргумента:

const myElement = document.querySelector ('# foo> div.bar')

Это вернет первое совпадение (сначала глубина). И наоборот, мы можем проверить, соответствует ли элемент селектору:

myElement.matches ('div.bar') === правда

Если мы хотим получить все вхождения, мы можем использовать:

const myElements = document.querySelectorAll ('. bar')

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

const myChildElemet = myElement.querySelector ('input [type = "submit"]') // Вместо // document.querySelector ('# foo> div.bar input [type = "submit"]')

Тогда зачем вообще использовать другие, менее удобные методы, такие как .getElementsByTagName ()? Ну, одно важное отличие состоит в том, что результат .querySelector () не является живым , поэтому, когда мы динамически добавляем элемент (см. раздел 3 подробности), который соответствует селектору, коллекция не будет обновляться.

const elements1 = document.querySelectorAll ('div') const elements2 = document.getElementsByTagName ('div') const newElement = document.createElement ('div') document.body.appendChild (newElement) elements1.length === elements2.length // ложный

Другое соображение заключается в том, что такая живая коллекция не должна иметь всю информацию заранее, тогда как .querySelectorAll () сразу собирает все в статический список, делая его менее производительный ,

Работа с ноделистами

Теперь есть две распространенные ошибки, касающиеся .querySelectorAll (). Во-первых, мы не можем вызывать методы Node для результата и распространять их на его элементы (как если бы вы могли использоваться из объектов jQuery). Скорее мы должны явно перебрать эти элементы. И это еще одна проблема: возвращаемое значение - NodeList, а не Array. Это означает, что обычные методы Array не доступны напрямую. Существует несколько соответствующих реализаций NodeList, таких как .forEach, которые, однако, все еще не поддерживаются ни одним IE. Поэтому мы должны сначала преобразовать список в массив или «позаимствовать» эти методы из прототипа Array.

// Использование Array.from () Array.from (myElements) .forEach (doSomethingWithEachElement) // Или до ES6 Array.prototype.forEach.call (myElements, doSomethingWithEachElement) // Сокращения: [] .forEach.call (myElements, doSomethingWithEachElement)

Каждый элемент также имеет несколько довольно понятных свойств только для чтения, ссылающихся на «семью», все из которых являются живыми:

myElement.children myElement.firstElementChild myElement.lastElementChild myElement.previousElementSibling myElement.nextElementSibling

Как Элемент интерфейс наследуется от Узел В интерфейсе также доступны следующие свойства:

myElement.childNodes myElement.firstChild myElement.lastChild myElement.previousSibling myElement.nextSibling myElement.parentNode myElement.parentElement

Где первые только ссылочные элементы, последние (за исключением .parentElement) могут быть узлами любого типа, например текстовыми узлами. Затем мы можем проверить тип данного узла, например, например,

myElement.firstChild.nodeType === 3 // это будет текстовый узел

Как и с любым объектом, мы можем проверить цепочку прототипов узла, используя оператор instanceof:

myElement.firstChild.nodeType instanceof Text

Модификация Классов и Атрибутов

Изменить классы элементов так же просто, как:

myElement.classList.add ('foo') myElement.classList.remove ('bar') myElement.classList.toggle ('baz')

Вы можете прочитать более глубокое обсуждение того, как изменять классы в этом быстрый совет от Yaphi Berhanu , Свойства элемента могут быть доступны как любые свойства других объектов

// Получить значение атрибута const value = myElement.value // Установить атрибут как свойство элемента myElement.value = 'foo' // Установить несколько свойств с помощью Object.assign () Object.assign (myElement, {value: 'foo ', id:' bar '}) // Удалить атрибут myElement.value = null

Обратите внимание, что есть также методы .getAttibute (), .setAttribute () и .removeAttribute (). Они напрямую изменяют атрибуты HTML (в отличие от свойств DOM) элемента, вызывая перерисовку браузера (вы можете наблюдать за изменениями, осматривая элемент с помощью инструментов разработчика вашего браузера). Такой перерисовка браузера не только дороже, чем просто установка свойств DOM, но и эти методы могут неожиданные результаты ,

Как правило, используйте их только для атрибутов, которые не имеют соответствующего свойства DOM (например, colspan), или если вы действительно хотите «сохранить» эти изменения в HTML (например, чтобы сохранить их при клонировании элемента или изменение родительского .innerHTML - см. раздел 3 ).

Добавление стилей CSS

Правила CSS могут применяться как любое другое свойство; Обратите внимание, что свойства в верблюде в JavaScript:

myElement.style.marginLeft = '2em'

Если нам нужны определенные значения, мы можем получить их через свойство .style. Однако это даст нам только те стили, которые были применены явно. Чтобы получить вычисленные значения, мы можем использовать .window.getComputedStyle (). Он берет элемент и возвращает CSSStyleDeclaration содержит все стили самого элемента, а также те, которые унаследованы от его родителей:

window.getComputedStyle (MyElement) .getPropertyValue ( 'Левое поле')

Модификация DOM

Мы можем перемещать элементы так:

// Добавить element1 как последний дочерний элемент element2 element1.appendChild (element2) // Вставить element2 как дочерний элемент 1, прямо перед element3 element1.insertBefore (element2, element3)

Если мы не хотим перемещать элемент, но вставляем копию, мы можем клонировать его следующим образом:

// Создать клон const myElementClone = myElement.cloneNode () myParentElement.appendChild (myElementClone)

Метод .cloneNode () может принимать логическое значение в качестве аргумента; если установлено значение true, будет создана глубокая копия, то есть ее потомки также будут клонированы.

Конечно, мы также можем создавать совершенно новые элементы или текстовые узлы:

const myNewElement = document.createElement ('div') const myNewTextNode = document.createTextNode ('некоторый текст')

который мы можем затем вставить, как показано выше. Если мы хотим удалить элемент, мы не можем сделать это напрямую, но мы можем удалить дочерние элементы из родительского элемента, например, так:

myParentElement.removeChild (MyElement)

Это дает нам небольшую работу вокруг, то есть фактически может косвенно удалить элемент, ссылаясь на его родительский элемент:

myElement.parentNode.removeChild (MyElement)

Свойства элемента

Каждый элемент также имеет свойства .innerHTML и .textContent (а также .innerText, который аналогичен .textContent, но имеет некоторые важные различия ). Они содержат HTML и текстовое содержимое соответственно. Это доступные для записи свойства, то есть мы можем напрямую изменять элементы и их содержимое:

// Заменить внутренний HTML myElement.innerHTML = `<div> <h2> Новое содержимое </ h2> <p> beep boop beep boop </ p> </ div>` // Удалить все дочерние узлы myElement.innerHTML = null // Добавить во внутренний HTML myElement.innerHTML + = `<a href="foo.html"> продолжить чтение ... </a> <hr />`

Добавление разметки в HTML, как показано выше, обычно является плохой идеей, поскольку мы потеряем все ранее сделанные изменения свойств для затронутых элементов (если только мы не сохранили эти изменения в виде атрибутов HTML, как показано в раздел 2 ) и связанные слушатели событий. Установка .innerHTML удобна для полного удаления разметки и замены ее чем-то другим, например, разметкой, выполняемой сервером. Поэтому добавление элементов лучше сделать так:

const link = document.createElement ('a') const text = document.createTextNode ('продолжить чтение ...') const hr = document.createElement ('hr') link.href = 'foo.html' link.appendChild ( текст) myElement.appendChild (ссылка) myElement.appendChild (час)

Однако при таком подходе мы вызовем две перерисовки браузера - по одной для каждого добавленного элемента - тогда как изменение .innerHTML вызывает только одну. Чтобы обойти эту проблему производительности, мы можем сначала собрать все узлы в DocumentFragment и затем просто добавьте этот фрагмент:

const фрагмент = документ.createDocumentFragment () фрагмент.appendChild (текст) фрагмент.appendChild (hr) myElement.appendChild (фрагмент)

Прослушивание событий

Это, возможно, самый известный способ привязки слушателя события:

myElement.onclick = function onclick (event) {console.log (event.type + 'получили уволены')}

Но этого, как правило, следует избегать. Здесь .onclick - это свойство элемента, означающее, что вы можете изменить его, но вы не можете использовать его для добавления дополнительных прослушивателей - переназначая новую функцию, вы перезаписываете ссылку на старую.

Вместо этого мы можем использовать гораздо более мощный метод .addEventListener (), чтобы добавить столько событий любого количества типов, сколько нам нужно. Он принимает три аргумента: тип события (например, щелчок), функция, которая вызывается всякий раз, когда событие происходит в элементе (эта функция получает объект события), и необязательный объект конфигурации, который будет объяснен ниже.

myElement.addEventListener ('click', function (event) {console.log (event.type + 'got fired')}) myElement.addEventListener ('click', function (event) {console.log (event.type + ') снова уволили ')})

В функции слушателя event.target ссылается на элемент, для которого было инициировано событие (как это происходит, если, конечно, мы не используем функция стрелки ). Таким образом, вы можете легко получить доступ к его свойствам следующим образом:

// Свойство `forms` документа представляет собой массив, содержащий // ссылки на все формы const myForm = document.forms [0] const myInputElements = myForm.querySelectorAll ('input') Array.from (myInputElements) .forEach (el => {el.addEventListener ('change', function (event) {console.log (event.target.value)})})

Предотвращение действий по умолчанию

Обратите внимание, что событие всегда доступно в функции слушателя, но хорошо бы явно передать его в любом случае, когда это необходимо (и мы можем назвать его, как нам нравится, конечно же). Не вдаваясь в подробности Мероприятие В самом интерфейсе, особенно заслуживающим внимания, является метод .preventDefault (), который, в свою очередь, предотвращает поведение браузера по умолчанию, например, переход по ссылке. Другим распространенным вариантом использования было бы условное предотвращение отправки формы, если проверка формы на стороне клиента не удалась.

myForm.addEventListener ('submit', function (event) {const name = this.querySelector ('# name') if (name.value === 'Donald Duck') {alert ('Ты должен быть шутишь!') событие .preventDefault ()}})

Другой важный метод события - это .stopPropagation (), который предотвратит всплывание события в DOM. Это означает, что если у нас есть прослушиватель щелчков (скажем), останавливающий распространение на элементе, и другой прослушиватель щелчков на одном из его родителей, событие щелчка, которое запускается на дочернем элементе, не сработает на родительском элементе - в противном случае, это сработало бы на обоих.

Теперь .addEventListener () принимает необязательный объект конфигурации в качестве 3-го аргумента, который может иметь любое из следующих логических свойств (все из которых по умолчанию имеют значение false):

  • capture: событие будет инициировано на элементе, прежде чем любой другой элемент под ним в DOM (захват и всплытие событий - это отдельная статья, подробнее см. Вот )
  • Once: Как вы можете догадаться, это означает, что событие будет запущено только один раз.
  • пассивный: это означает, что event.preventDefault () будет игнорироваться (и обычно выдает предупреждение в консоли)

Наиболее распространенным вариантом является .capture; на самом деле, это настолько распространено, что для этого есть сокращение: вместо того, чтобы указывать его в объекте конфигурации, вы можете просто передать здесь логическое значение:

myElement.addEventListener (type, listener, true)

Прослушиватели событий могут быть удалены с помощью .removeEventListener (), который принимает тип события и ссылку на функцию обратного вызова, которую необходимо удалить; например, опция Once также может быть реализована как

myElement.addEventListener ('change', слушатель функции (событие) {console.log (event.type + 'был вызван на' + this) this.removeEventListener ('change', listener)})

Делегация мероприятия

Другой полезный шаблон - делегирование событий : скажем, у нас есть форма, и мы хотим добавить прослушиватель события изменения для всех входных дочерних элементов. Один из способов сделать это - перебирать их, используя myForm.querySelectorAll ('input'), как показано выше. Однако в этом нет необходимости, когда мы можем просто добавить его в саму форму и проверить содержимое файла event.target.

myForm.addEventListener ('change', function (event) {const target = event.target if (target.matches ('input')) {console.log (target.value)}})

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

Анимация

Обычно самый чистый способ выполнения анимации - это применять классы CSS со свойством перехода или использовать CSS @keyframes. Но если вам нужна большая гибкость (например, для игры), это можно сделать и с помощью JavaScript.

Наивным подходом было бы вызывать саму функцию window.setTimeout () до тех пор, пока нужная анимация не будет завершена. Однако это неэффективно вызывает быстрое перепечатывание документов; и эта раскрутка макета может быстро привести к заиканию, особенно на мобильных устройствах. Кроме того, мы можем синхронизировать обновления с помощью window.requestAnimationFrame (), чтобы запланировать все текущие изменения в следующем кадре перерисовки браузера. Он принимает обратный вызов в качестве аргумента, который получает текущую метку времени (высокое разрешение):

const start = window.performance.now () const duration = 2000 window.requestAnimationFrame (функция fadeIn (сейчас)) {const progress = now - запустить myElement.style.opacity = прогресс / продолжительность if (progress <duration) {window.requestAnimationFrame (fadeIn)}}

Таким образом, мы можем получить очень плавную анимацию. Для более подробного обсуждения, пожалуйста, посмотрите на это статья Марка Брауна.

Написание собственных вспомогательных методов

Правда, всегда нужно перебирать элементы, чтобы что-то с ними сделать, может быть довольно громоздким по сравнению с лаконичным и цепным синтаксисом jQuery $ ('. Foo'). Css ({color: 'red'}). Так почему бы просто не написать наши собственные сокращенные методы для таких вещей?

const $ = function $ (селектор, контекст = документ) {const elements = Array.from (context.querySelectorAll (selector)) return {elements, html (newHtml) {this.elements.forEach (element => {element.innerHTML = newHtml}) вернуть this}, css (newCss) {this.elements.forEach (element => {Object.assign (element.style, newCss)}) вернуть this} on (событие, обработчик, параметры) {this.elements .forEach (element => {element.addEventListener (событие, обработчик, опции)}) вернуть это} // и т. д.}}

Таким образом, у нас есть сверхтонкая DOM-библиотека с только теми методами, которые нам действительно нужны, и без веса обратной совместимости. Обычно у нас есть эти методы в прототипе нашей коллекции. Вот (несколько более сложный) суть с некоторыми идеями, как реализовать таких помощников. В качестве альтернативы, мы можем сделать это так просто, как

const $ = (селектор, контекст = документ) => context.querySelector (селектор) const $$ = (селектор, контекст = документ) => context.querySelectorAll (селектор) const html = (nodeList, newHtml) => {Массив. from (nodeList) .forEach (element => {element.innerHTML = newHtml})} // И так далее ...

демонстрация

Чтобы завершить эту статью, вот CodePen, который демонстрирует многие из концепций, объясненных выше, для реализации простой техники лайтбокса. Я призываю вас потратить некоторое время на просмотр исходного кода и сообщить мне в комментариях ниже, если у вас есть какие-либо замечания или вопросы.

Увидеть перо Tightbox по SitePoint ( @SitePoint ) на CodePen ,

Заключение

Я надеюсь, что смогу показать, что манипулирование DOM простым JavaScript не является ракетостроением, и что на самом деле многие методы jQuery имеют прямые эквиваленты в нативном API DOM. Это означает, что для некоторых случаев повседневного использования (таких как навигационное меню или модальное всплывающее окно) дополнительные издержки библиотеки DOM могут быть неуместны.

И хотя это правда, что некоторые части нативного API являются многословными или неудобными (например, приходится постоянно перебирать списки узлов вручную), мы можем довольно легко написать наши собственные небольшие вспомогательные функции для абстрагирования таких повторяющихся задач.

Но теперь это для вас. Как вы думаете? Вы предпочитаете избегать сторонних библиотек там, где можете, или же ваши собственные библиотеки просто не стоят когнитивных накладных расходов? Позвольте мне знать в комментариях ниже.

Эта статья была рецензирована Вилдан Софтик а также Джоан Инь , Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

Похожие

Работа - Opiekunka, świętokrzyskie - 39
Получить последние предложения о работе для этого поиска по электронной почте : Создавая уведомление, вы принимаете наши T & Cs и Уведомление о конфиденциальности и вы соглашаетесь на использование куки. Результаты 1-10 из 39 Показывать на сайте и сортировать по ИНТЕРКАДРА - DALESZYCE, ŚWIĘTOKRZYSKIE
Microsoft Lumia 650
... по-настоящему . ТЕХНИЧЕСКИЕ ХАРАКТЕРИСТИКИ Microsoft Lumia 650 размеры масса 142 х 70,9 х 6,9 мм 122 г дисплей 5 "OLED 720 x 1280 (~ 297 ppi); ClearBlack; Gorilla Glass 3 CPU / GPU четырехъядерный
Как сбросить стили вашего сайта с помощью CSS Reset
... умолчанию. настройки. Сбрасывая стили, вы избегаете использования по умолчанию встроенных стилей браузера, которые отличаются от браузера к браузеру. CSS Reset позволяет избежать несоответствий браузера Например, допустим, вы используете тег привязки <a> в своем HTML-документе. Обычно браузеры, такие как Internet Explorer и Firefox, отображают его синим и подчеркнутым . Но через пять лет кто-то решает выпустить новый браузер
CSS Masking - HTML5 Rocks
... спользуются две операции: вырезание и маскирование. Обе операции скрывают визуальные части элемента. Если вы работали с SVG или HTML Canvas раньше, эти операции, вероятно, не новы для вас. Отсечение определяет область видимого элемента. Все вокруг этого региона не отображается - оно «обрезается». При маскировании изображение маски комбинируется с элементом, влияющим на альфа-канал этого
Работа в бухгалтерии
Профессия бухгалтера была когда-то регламентирована. Его выполнение требуется не только для получения основного образования, но и для подтверждения состояния знаний во время государственного экзамена. В настоящее время это проще, и с бухгалтерами могут также обращаться люди, которые отвечают ограничительным условиям.
Huawei P8 - Обзор
Huawei специализируется на чрезвычайно тонких устройствах в премиальном сегменте смартфонов, как и их ежегодный Huawei P8 без названия Ascend. Что такое Huawei P8, что он предлагает и что не так с этим подробным обзором. ТЕХНИЧЕСКИЕ ХАРАКТЕРИСТИКИ Huawei
Обучение HTML и CSS на практике - Модуль I: прочная основа
... по HTML и CSS и первой частью курса веб-дизайнера . В течение нескольких дней обучения мы покажем вам, что HTML и CSS не являются черной магией, как вы могли подумать поначалу. Во время практического обучения HTML и CSS - Модуль I: Основы основ вы изучите основы проектирования и создания эстетичных и функциональных веб-сайтов. Доступным способом, шаг за шагом знакомясь со знаниями, необходимыми для создания
Free Slider - jQuery Slider Скачать бесплатно
... умолчанию. У меня также есть ряд коммерческих сайтов. Я собирался спросить о скидках, но потом я заметил ваш бесплатный скачиваемый пакет Slider, который действительно привлек мое внимание. Мне показалось бы немного глупо искать скидку, когда стоимость больше, чем там! Дайте мне знать о публикации в Drive Folders, и я буду в порядке. Я могу опубликовать на диске сейчас,
Что такое блог и как его создать?
... событий или сетевой журнал») - это сайт, на котором записи, мультимедиа или изображения, которые добавляются регулярно, является основным содержанием. Отличается блог от сайта достаточно просто. Во-первых, блог как правило посвящается определенной тематике (от манимейкинга к астрологии). Во-вторых, блоги в свободном доступе и к каждой записи читатели имеют возможность оставить комментарий. Ну и, в-третьих, на главных страницах блога есть записи (посты), которые публикуются в обратном
Обзор Dell XPS 15: Core i9-8950HK + графика Nvidia + 4K HDR + 32 ГБ ОЗУ = зверь
Я всегда был поклонником Dell XPS 15. Честно говоря, не так много портативных ПК, в которых используются процессоры Intel серии H, которые не представлены в качестве игровых ПК или рабочих станций. Это все еще больше похоже на ультрабук, но с большой силой, чтобы поддержать это. Модель, присланная мне Dell, включает в себя процессор Core i9-8950HK, 32 ГБ ОЗУ, графический процессор Nvidia GeForce GTX 1050Ti и 15,6-дюймовый дисплей 4K HDR. Это мечта, которую можно использовать, и в ней,
Топ 5 Материалов Дизайн Рамки и библиотеки
... DOM Библиотека JavaScript может быть загружена асинхронно Кроссплатформенная поддержка (например, HTML Email, React, WebComponents) Экстремальный взлом Скачать полимер «Polymer не
GetElementsByTagName ()?
Так почему бы просто не написать наши собственные сокращенные методы для таких вещей?
Как вы думаете?
Вы предпочитаете избегать сторонних библиотек там, где можете, или же ваши собственные библиотеки просто не стоят когнитивных накладных расходов?
Новости
Провайдеры:
  • 08.09.2015

    Batyevka.NET предоставляет услуги доступа к сети Интернет на территории Соломенского района г. Киева.Наша миссия —... 
    Читать полностью

  • 08.09.2015
    IPNET

    Компания IPNET — это крупнейший оператор и технологический лидер на рынке телекоммуникаций Киева. Мы предоставляем... 
    Читать полностью

  • 08.09.2015
    Boryspil.Net

    Интернет-провайдер «Boryspil.net» начал свою работу в 2008 году и на данный момент является одним из крупнейших поставщиков... 
    Читать полностью

  • 08.09.2015
    4OKNET

    Наша компания работает в сфере телекоммуникационных услуг, а именно — предоставлении доступа в сеть интернет.Уже... 
    Читать полностью

  • 08.09.2015
    Телегруп

    ДП «Телегруп-Украина» – IT-компания с 15-летним опытом работы на рынке телекоммуникационных услуг, а также официальный... 
    Читать полностью

  • 08.09.2015
    Софтлинк

    Высокая скоростьМы являемся участником Украинского центра обмена трафиком (UA — IX) с включением 10 Гбит / сек... 
    Читать полностью