Подписывайтесь на мой твиттер, там всегда что-нибудь интересное!

15 методов работы с элементами про которые вы возможно не слышали

В этой статье вы узнаете про 15 методов работы с элементами про которые вы возможно не слышали.

Перевод статьи — 15 HTML element methods you’ve potentially never heard of

Для новичков

Давайте обсудим разницу между HTML и DOM.

Старый добрый элемент <table> это HTML, что очевидно. Вы можете использовать его в файле с расширением .html. Он имеет набор атрибутов, которые влияют на вид и поведение страницы.

И это все, что можно о нем сказать. У него нет ничего общего с JavaScript.

DOM это то, что соединяет ваш JavaScript код с HTML элементами в документе, так что вы можете взаимодействовать с этими элементами, как с объектами.

Это и есть объектная модель документа, то есть DOM.

Каждый тип элемента в HTML имеет свой собственный ‘интерфейс’, который определяет свойства (который обычно сопоставляются с атрибутами HTML элемента) и методы. Для примера, <table> имеет интерфейс под названием HTMLTableElement.

Вы можете сослаться на конкретный элемент, написав что-то типа того:

const searchBox = document.getElementById('search-box');

Далее у вас будет доступ ко всем свойствам и методам, которые доступны для этого типа элемента. Для примера, вы можете получить доступ к свойству value с помощью searchBox.value или направить курсор на поле поиска, вызвав searchBox.focus().

Спасибо за посещение моего 58-ми секундного курса DOM.

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

Но к счастью для вас, рысканье по спецификациям и написание статей, это два моих любимых способа борьбы со злом. И так поехали.

Если вы хотите поиграться с кодом и у вас есть DevTools, то вы можете выбрать элемент в древе элементов, затем напечатать $0 в консоле. Это даст вам отсылку к элементу, который вы выбрали. Чтобы увидеть элемент, как объект, введите dir($0).

Вообще вы можете много всего сделать в консоли.

1. Табличные методы

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

Вот несколько из них.

const tableEl = document.querySelector('table');

const headerRow = tableEl.createTHead().insertRow();
headerRow.insertCell().textContent = 'Make';
headerRow.insertCell().textContent = 'Model';
headerRow.insertCell().textContent = 'Color';

const newRow = tableEl.insertRow();
newRow.insertCell().textContent = 'Yes';
newRow.insertCell().textContent = 'No';
newRow.insertCell().textContent = 'Thank you';

Ни единого document.createElement().

.insertRow() метод даже вставит <tbody> для вас, если вы вызовите его прямо на табличном элементе. Не отлично ли это?

2. scrollIntoView()

Вы знаете, что когда у вас есть #something в ссылке и когда страница загружается, браузер проскролит страницу, так что вы сможете увидеть элемент с этим ID?

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

document.querySelector(document.location.hash).scrollIntoView();

3. hidden

Ок, это не метод, но если вы поспорите, то я возражу в ответ тем, что это между прочим это скорее сеттер и выходит, что это действительно метод, правильно?

В любом случае, вы когда нибудь делали myElement.style.display = "none", для того, чтобы спрятать элемент? Отлично, прекратите так делать.

Вы можете просто сделать так myElement.hidden = true.

4. toggle()

Ок, это не совсем метод элемента, а скорее метод для свойства элемента. А точнее, это метод для переключения добавления/удаления класса с элемента с помощью myElement.classList.toggle("some-class")

И если вы когда-либо добавляли класс с условным объявлением if, то это преступление — не иначе.

Вам нужно просто передать второй параметр методу toggle. И когда дойдет до дела, ваш класс будет добавлен элементу.

el.classList.toggle('some-orange-class', theme === 'orange');

Я знаю, что вы думаете, то что это не совсем то, что означает понятие “переключать” — и хорошие люди на Internet Explorer согласятся и будут протестовать, не применяя логику второго параметра вообще.

В общем, я беру свои слова назад, это не преступление. Выбор за вами.

5. querySelector()

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

Для примера, myElement.querySelector(".my-class") выберет только элементы, которые имеют класс my-class и они являются потомками myElement.

6. closest

Это метод доступный на всех элементах, которые идут выше в древе элементов. Это как реверс querySelector(). Итак, я бы мог выбрать заголовок данной секции в таком исполнении:

myElement.closest("article").querySelector("h1");

Вверх до первого <article> и затем назад вниз к первому заголовку <h1>.

7. getBoundingClientRect()

Этот метод возвращает аккуратный маленький объект с некоторыми деталями, а точнее пространственными деталями элемента, который вы вызываете.

{
  x: 604.875,
  y: 1312,
  width: 701.625,
  height: 31,
  top: 1312,
  right: 1306.5,
  bottom: 1343,
  left: 604.875
}

Будьте аккуратны в двух случаях:

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

Не все браузеры возвращают эти значения. Потому что с чего бы это вдруг?

8. matches()

Скажем, что я хочу проверить, есть ли у конкретного элемента конкретный класс.

Максимум сложности:

if (myElement.className.indexOf('some-class') > -1) {
  // do something
}

Уже лучше, но ничего пока особо не поменялось:

if (myElement.className.includes('some-class')) {
  // do something
}

Лучше всего:

if (myElement.matches('.some-class')) {
  // do something
}

9. insertAdjacentElement()

Этот я выучил только сегодня! Он как appendChild(), но даёт больше контроля, когда вы дополняете элемент потомком.

parentEl.insertAdjacentElement("beforeend", newEl) это буквально тоже самое, что и parentEl.appendChild(newEl), но вы также можете указать beforebeginafterbegin или afterend, чтобы вставить элемент в место, которое оно предполагает по названию.

Какой контроль!

10. contains()

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

Для примера, если я работаю с кликом мышки и хочу знать, случилось ли это внутри модалки или вне её, то я могу сделать так:

const handleClick = e => {
  if (!modalEl.contains(e.target)) modalEl.hidden = true;
};

Где modalEl это сам подал, а e.target будет любым элементом на который кликнули.

Забавный факт, 100% времени, что я делаю это, у меня получается противоположно неверная логика при первой попытке.

Даже если я стану умнее и переключусь на другой способ перед кликом на save, я всё равно сделаю это неправильно.

11. getAttribute()

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

Помните, когда я упоминал выше, что свойства обычно сопоставляются атрибутам?

Один из примеров, когда это не так — происходит с атрибутом href, такого элемента как <a href="/animals/cat">Cat</a>.

el.href не вернет /animals/cat, как вы уже могли предположить. Так происходит из-за того, что элемент <a> выполняет HTMLHyperlinkElementUtils интерфейс, который имеет группу вспомогательных свойств, таких как protocol и hash, которые говорят вам о том куда ведет ссылка.

Одно из таких вспомогательных свойств это href, которое выдаст вам полный URL, со всеми штуками, которые не относятся к атрибуту URL.

Так что вам надо будет использовать el.getAttribute("href"), если вам нужна конкретная строка из атрибута href.

12. Трио для элемента <dialog>

У сравнительно нового элемента <dialog> есть два нормальных метода и один ну просто восхитительный. show() и close() сделают именно то, что вы ожидаете от них.

Но showModal() покажет <dialog> выше всего, отцентрованным на странице, как вы и хотите, чтобы показывались ваши формы. Нет нужды в z-index или ручного добавления затемняющего фона, или отслеживания кнопки esc, чтобы его закрыть, браузер знает, как работают формы и сделает это все за вас.

И это просто отлично.

13. forEach()

Иногда, когда вы ссылаетесь на список элементов, вы можете пройтись по всем ним с помощью forEach().

for() циклы стали такими винтажными.

Скажем так, вам нужно выгрузить URL для всех ссылок на странице. Вы бы могли сделать так, если бы захотели увидеть ошибку.

document.getElementsByTagName('a').forEach(el => {
    console.log(el.href);
});

Или вы бы могли сделать так:

document.querySelectorAll('a').forEach(el => {
    console.log(el.href);
});

Так произойдет, потому что getElementsByTagName и другие получают от методов HTMLCollection, а querySelectorAll отдаст NodeList.

И это интерфейс NodeList, который дает нам метод forEach(), вместе с keys()value() и entries().

На самом деле было бы хорошо, если бы все просто возвращали массивы, вместо того, чтобы вы отдавать что-то фантастическое, не совсем похожее на них. Но не пугайтесь, потому что хороший парень из ECMA дал нам Array.from(), который превращает любой “около массив” в массив. И это работает:

Array.from(document.getElementsByTagName('a')).forEach(el => {
    console.log(el.href);
});
Array.from(document.querySelectorAll('a'))
  .map(el => el.origin)
  .filter(origin => origin !== document.origin)
  .filter(Boolean);

14. Формы

У <form>, как вы уже наверное знаете, есть метод submit(). Возможно вы также знаете, что у форм есть метод reset() и они могут reportValidity(), если вы используете валидацию на ваших элементах формы.

Вы также можете использовать свойства элементов формы с точечным обозначением для отсылки к элементу по его имени атрибута. Для примера, myFormEl.elements.email вернул бы элемент <input name="email" />, который принадлежит <form>.

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

Пример: если у вас есть три радио кнопки, каждая с одинаковым именем animal, то formEl.elements.animal будет выбирать сбор радио кнопок (1 контрол, 3 элемента).

И formEl.elements.animal.value вернет значение любой выбранной кнопки.

Это странный синтаксис, если вы уже подумали об этом. Давайте разберем его: formEl это элемент, elements это HTMLFormControlsCollection, не реальный массив в котором каждый элемент не обязательно является HTML элементом. animal это набор нескольких радио кнопок, совмещенных только потому что у них есть один и тот же атрибут name (есть RadioNodeList интерфейс именно для этой цели) и value, которое смотрит за value атрибутом, какая бы радио кнопка не была выбрана.

15. select()

Мне возможно нужно было заранее сказать, так как последний не такой уж и интересный. В любом случае, .select() метод выберет весь текст в любом инпуте, в котором бы вы его не вызвали.