Какие свойства у элементов javascript

Какие свойства у элементов javascript thumbnail

В этой статье познакомимся DOM-свойствами и атрибутами, рассмотрим, чем они отличаются и как правильно с ними работать. Разберём какие в JavaScript есть методы для выполнения операций над атрибутами.

Чем отличается атрибут от DOM-свойства?

Атрибуты — это HTML-сущности, с помощью которых мы можем добавить определённые данные к элементам в HTML-коде.

Когда браузер запрашивает некоторую страницу, он получает её исходный HTML-код. После этого он парсит этот код и строит на его основании DOM. Во время этого процесса HTML-атрибуты элементов переводятся в соответствующие DOM-свойства.

Например, браузер, при чтении следующей HTML-строчки кода, создаст для этого элемента следующие DOM-свойства: id, className, src и alt.

<img id=»brand» class=»brand» src=»logo.png» alt=»логотип сайта»>

Обращение к этим свойствам в коде JavaScript выполняется как к свойствам объекта. Объектом здесь выступает узел (элемент) DOM.

Пример, в котором получим значения DOM-свойств для элемента, приведённого выше, и выведем их значения в консоль:

// получим элемент
var brandImg = document.querySelector(‘#brand’);

// выведем в консоль значения DOM-свойств элемента
console.log(brandImg.id); // «brand»
console.log(brandImg.className); // «brand»
console.log(brandImg.src); // «logo.png»
console.log(brandImg.alt); // «логотип сайта»

Некоторые названия DOM-свойств не соответствуют именам атрибутов. Одним из таких является атрибут class. Данному атрибуту соответствует DOM-свойство className. Данное отличие связано с тем, что class является ключевым словом в JavaScript, оно зарезервировано и не может использоваться. Из-за этого разработчики стандарта решили использовать для соответствия какое-то другое название, в качестве которого было выбрано className.

Ещё один нюанс связан с тем, что перевод HTML-атрибутов, заданных в исходном коде документа, в DOM-свойства не всегда осуществляется один к одному.

Если элемент имеет нестандартный HTML-атрибут, то свойство, соответствующее ему в DOM, не создаётся.

<div id=»mydiv» alt=»…»></div>

<script>
// получим элемент
mydiv = document.querySelector(‘#mydiv’);
// получим значение свойство alt элемента и выведем его в консоль
console.log(mydiv.alt); // undefined
// получим значение атрибут alt элемента и выведем его в консоль
console.log(mydiv.getAttribute(‘alt’)); // «…»
</script>

Другое отличие связано с тем, что значения определённых HTML-атрибутов и соответствующих им DOM-свойств могут быть различными. Т.е. атрибут может иметь одно значение, а DOM-свойство, созданное на его основе – другое.

Одним из таких атрибутов является checked.

<input type=»checkbox» checked>

Значение HTML-атрибута checked в данном случае – это пустая строка. Но, свойство, соответствующее данному атрибуту в DOM, будет иметь значение true. Т.к. по правилам стандарта для установления true достаточно лишь упоминание этого атрибута в HTML-коде и при этом не важно какое он будет иметь значение.

При этом даже если мы в HTML-коде не укажем атрибут checked для элемента input с типом checkbox, то для него в DOM всё равно будет создано свойство checked, но оно будет равно false.

<input type=»checkbox»>

Кроме этого, JavaScript позволяет также работать с атрибутами. Для этого в DOM API имеются специальные методы. Но их желательно использовать только тогда, когда вам действительно нужно работать с данными именно так.

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

Основные отличия между DOM-свойствами и атрибутами:

  • значение атрибута – это всегда строка, а значение DOM-свойства – определённый тип данных (не обязательно строка);
  • имя атрибута – регистронезависимо, а DOM-свойства — регистрозависимо. Т.е. в HTML-коде мы можем, например, HTML-атрибут id написать, как Id, ID и т.д. То же касается и имени атрибута, которые мы указываем в специальных методах JavaScript для работы с ним. Но к соответствующему DOM-свойству мы можем обратиться только по id и никак по-другому.

Работа с DOM-свойствами элемента

Работа со свойствами элементов в JavaScript как уже было отмечено выше осуществляется как со свойствами объектов.

Но для того, чтобы обратиться к свойству некоторого элемента, его необходимо сначала получить. Получить DOM-элемент в JavaScript можно, например, с помощью универсального метода querySelector, а коллекцию DOM элементов, например, посредством querySelectorAll.

В качестве первого примера рассмотрим следующий HTML-элемент:

<div id=»alert» class=»alert alert-info» title=»Текст подсказки…»>
Текст информационного сообщения…
</div>

<script>
var alert = document.querySelector(‘#alert’); // получим элемент
</script>

На базе него разберём как осуществляется получение DOM-свойств, их изменение и добавление новых.

Чтение значений DOM-свойств:

// получим значение DOM свойства id
var alertId = alert.id; // «alert»
// получим значение DOM свойства className
var alertClass = alert.className; // «alert alert-info»
// получим значение DOM свойства title
var alertId = alert.title; // «Текст подсказки…»

Читайте также:  Какая ядерная структура несет наследственные свойства организма

Изменение значений DOM-свойств:

// для изменения значения DOM свойству, ему нужно просто присвоить новое значение
alert.title = «Новый текст подсказки»; // присвоим DOM-свойству title элемента новое значение
// или так (т.к. обращение к этому свойству мы уже сохранили в переменную alertId)
alertId = «Новый текст подсказки»;
// или так (т.к. обращение к этому свойству мы уже сохранили в переменную alertId)
alert.className = «alert alert-warning»;

Добавление DOM-свойств:

alert.lang = «ru»; // установим свойству lang значение равное «ru»
alert.dir = «ltr»; // установим свойство dir значение равное «ltr»

Пример, в котором выведем в консоль все значения классов, которые есть у элементов p на странице:

var paragraphs = document.querySelectorAll(«p»);
for (var i = 0, length = paragraphs.length ; i < length; i++) {
if (paragraphs[i].className) {
console.log(paragraphs[i].className);
}

Пример, в котором установим всем элементам с классом content свойство lang со значением «ru»:

var contents = document.querySelectorAll(‘.content’);
for (var i = 0, length = contents.length; i < length; i++) {
contents[i].lang = «ru»;
}

Атрибуты элементов и методы для работы с ними

Атрибуты изначально задаются в HTML-коде. Они хоть и связаны, некоторым образом, со свойствами, но это не одно и тоже. В большинстве случаев следует работать именно со свойствами, а к атрибутам обращаться только тогда, когда это действительно нужно.

Значения атрибутов, в отличие от DOM-свойств, как это уже было отмечено выше всегда является строкой.

В JavaScript для выполнения операций, связанных с атрибутами, имеется четыре метода:

  • .hasAttribute(‘имя_атрибута’) – проверяет наличие указанного атрибута у элемента. Если проверяемый атрибут есть у элемента, то данный метод возвращает true, в противном случае — false.
  • .getAttribute(‘имя_атрибута’) – получает значение атрибута. Если указанного атрибута нет у элемента, то данный метод возвращает пустую строку («») или null.
  • .setAttribute(‘имя_атрибута’, ‘значение_атрибута’) – устанавливает указанный атрибут с указанным значением элементу. Если указанный атрибут есть у элемента, то данный метод тогда просто изменит ему значение.
  • .removeAttribute(‘имя_атрибута’) — удаляет указанный атрибут у элемента.

Рассмотрим примеры.

Очень интересный пример с атрибутом value.

Пример с атрибутом value

<input name=»name» type=»text» value=»Bob»>

<script>
var name = document.querySelector(‘input[name=»name»]’); // получим элемент
</script>

Получим значение атрибута value и DOM-свойства value:

// получим значение атрибута value у элемента
name.getAttribute(‘value’); // «Bob»
// получим значение DOM-свойства value
name.value; // «Bob»

// обновим значение атрибута value, установим ему новое значение
name.setAttribute(‘value’, ‘Tom’); // «Tom»
// получим значение DOM свойства value
name.value; // «Tom»

Из этого примера видно, что, при измении атрибута value, браузер автоматически изменяет в соответствии с ним DOM-свойство value.

Теперь давайте проделаем действия, наоборот, а именно изменим значение DOM-свойства и проверим изменится ли значение атрибута:

// установим новое значение DOM-свойству value
name.value = «John»;
// получим значение атрибута value у элемента
name.getAttribute(‘value’); // «Tom»

Из этого примера видно, что не всегда изменение DOM-свойства приводит к соответствующему изменению атрибута. Т.е. в этом случае изменение DOM-свойства value не изменяет соответствующий ему атрибут.

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

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

Даже в случае, когда вам нужно получить начальное значение value, которое мы установили в HTML, можно воспользоваться свойством. Свойство, содержащее начальное значение атрибута value называется defaultValue.

name.defaultValue; // Tom

Ещё один очень интересный пример, но теперь с атрибутом href.

Пример с атрибутом href

Пример, в котором нам нужно получить значение ссылки так, как оно было установлено в HTML.

<a id=»link» href=»page2.html»></a>

<script>
var page2 = document.querySelector(‘#link’);
page2.getAttribute(‘href’); // page2.html
page2.href; // полный URL, например: https://localhost/page2.html
</script>

В этом примере атрибут href и DOM-свойство href содержат разные значения. В атрибуте href — то, что мы установили в коде, а в DOM-свойстве — полный URL. Это различие продиктовано стандартом, в соответствии с которым браузер должен привести значение href к полному URL.

Поэтому если нам нужно получить то, что находится в атрибуте, то без метода getAttribute в этом случае не обойтись.

В завершении разберём ещё атрибут selected.

Пример с атрибутом selected

Пример, в котором показано как можно получить значение выбранной опции select:

<select id=»mark»>
<option value=»none» selected>нет оценки</option>
<option value=»1″>1</option>
<option value=»2″>2</option>
<option value=»3″>3</option>
<option value=»4″>4</option>
<option value=»5″>5</option>
</select>

Читайте также:  Какого цвета нефрит камень свойства

<script>
// получаем элемент select
var mark = document.querySelector(‘#mark’);
// 1 способ
mark.querySelector(‘option:checked’).value;
// 2 способ
mark.value;
</script>

Пример, в котором показано как можно получить выбранные значения опций в элементе select:

<select id=»mark» multiple>
<option value=»none» selected>нет оценки</option>
<option value=»1″>1</option>
<option value=»2″>2</option>
<option value=»3″>3</option>
<option value=»4″>4</option>
<option value=»5″>5</option>
</select>

<script>
// получаем элемент select
var mark = document.querySelector(‘#mark’);
// 1 способ (через создание массива и наполнением его значений выбранных опций)
var arr = [];
for (var i = 0, length = mark.options.length; i < length; i++) {
if (mark.options[i].selected) {
arr.push(mark.options[i].value);
}
}
// 2 способ (более современный, с использованием DOM-свойства selectedOptions)
var arr = Array.from(mark.selectedOptions, option => option.value)
</script>

Ещё один способ работы с атрибутами (свойство attributes)

В JavaScript у каждого элемента имеется свойство attributes, с помощью которого можно получить все его атрибуты в виде объекта NamedNodeMap.

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

Доступ к атрибуту в этой коллекции осуществляется по его индексу или с помощью метода item. Отсчёт атрибутов в этой коллекции ведётся с 0.

Например, выведем в консоль все атрибуты некоторого элемента:

<p id=»message» class=»text» style=»text-align: center;»>I LOVE JAVASCRIPT</p>

<script>
// получим элемент по его идентификатору message
var message = document.querySelector(‘#message’);
// получим его атрибуты
var attrs = message.attributes;
// переберём все атрибуты с помощью цикла (attrs.length – количество атрибутов)
for (var i = 0, length = attrs.length; i < length; i++) {
// attrs[i] или attrs.item(i) – обращение к атрибуту в коллекции по его порядковому номеру
// attrs[i].name – имя атрибута
// attrs[i].value – значение атрибута (с помощью него можно также изменить значение атрибута)
console.log(attrs[i].name + ‘ = ‘ + attrs[i].value);
// или с помощью метода item
console.log(attrs.item(i).name + ‘ = ‘ + attrs.item(i).value);
// пример как можно изменить значение через свойство value
// if (attrs[i].name === «class») {
// attr[i].value = «info»;
// }
}

// в результате выполнения:
// id = message
// class = text
// style = text-align: center;

</script>

Кроме этого, работать с этой коллекцией можно также посредством следующих методов:

  • .getNamedItem(‘имя_aтpибyтa’) – получает значение указанного атрибута (если указанный атрибут отсутствует у элемента, то в качестве результата получим null).
  • .setNamedItem(‘aтpибyт_yзeл’) – добавляет новый атрибут к элементу или обновляет значение у существующего. Для создания атрибута необходимо использовать метод document.createAttribute(), которому в качестве параметра необходимо передать имя атрибута. После этого созданному атрибуту необходимо присвоить значение с помощью свойства value.
  • .removeNamedItem(‘имя_атрибута’) – удаляет указанный атрибут у элемента (в качестве результата возвращает удалённый атрибут).

Пример, работы с атрибутами через методы getNamedItem, setNamedItem и removeNamedItem:

<p id=»message» class=»text» style=»text-align: center;»>I LOVE JAVASCRIPT</p>

<script>
// получим элемент по его идентификатору message
var message = document.querySelector(‘#message’);
// получим его аттрибуты
var attrs = message.attributes;
// Задача №1. Получим значение атрибута id
console.log(attrs.getNamedItem(‘id’));
// Задача №2. Установим атрибут (если он есть, то изменим его значение, в противном случае добавим новый)
// создаём атрибут style и сохраняем его в переменную attrStyle
var attrStyle = document.createAttribute(‘style’);
// присваиваем значение атрибуту с помощью свойства value
attrStyle.value = ‘text-align: left;’;
// устанавливаем атрибут элементу
attrs.setNamedItem(attrStyle);
// Задача №3. удалить атрибут class у элемента
attrs.removeNamedItem(«class»);
</script>

Задачи

  • Вывести в консоль все элементы документа, которые имеют атрибут id.
  • Добавить атрибут title ко всем изображениям на странице, если данного атрибута у них нет. Значение атрибута установить равным значению атрибута alt.

Источник

В случае JavaScript-движка V8 — очень даже. В этой статье я привожу результаты своего маленького исследования эффективности одной из внутренних оптимизаций V8.

Описание механизма V8

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

Если очень упростить, то V8 строит объекты используя внутренние скрытые классы. Каждый такой класс соответствует уникальной структуре объекта. Например, если у нас есть такой код:

// Создаётся базовый скрытый класс, с условным названием C0
const obj = {}

// Создается новый класс с описанием поля «a», условным названием С1 и ссылкой на С0
obj.a = 1

// Создается новый класс с описанием поля «b», условным названием С2 и ссылкой на С1
obj.b = 2

// Создается новый класс с описанием поля «c», условным названием С3 и ссылкой на С2
obj.c = 3

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

Читайте также:  Какие частицы определяют свойства вещества

// Базовый класс уже существует — C0
const obj2 = {}

// Класс описывающий поле «a» и ссылающийся на C0 уже существует — С1
obj2.a = 4

// Класс описывающий поле «b» и ссылающийся на C1 уже существует — С2
obj2.b = 5

// Класс описывающий поле «c» и ссылающийся на C2 уже существует — С3
obj2.c = 6

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

// Базовый класс уже существует — C0
const obj3 = {}

// Не существует класса описывающего поле «c» и ссылающегося на C0.
// Будет создан новый — С4
obj3.c = 7

// Не существует класса описывающего поле «a» и ссылающегося на C4.
// Будет создан новый — С5
obj3.a = 8

// Не существует класса описывающего поле «a» и ссылающегося на C5.
// Будет создан новый — С6
obj3.b = 9

Далее я постарался протестировать и определить разницу в производительности при работе с объектами в которых имена полей совпадают и в которых они отличаются.

Метод тестирования

Я провел три группы тестов:

  1. Static — имена свойств и их порядок не изменялись.
  2. Mixed — изменялась только половина свойств.
  3. Dynamic — все свойства и их порядок были уникальными для каждого объекта.

  • Все тесты запускались на NodeJS версии 13.7.0.
  • Каждый тест запускался в отдельном, новом потоке.
  • Все ключи объектов имеют одинаковую длину, а все свойства одинаковое значение.
  • Для измерения времени выполнения использовался Performance Timing API.
  • В показателях могут быть незначительные колебания. Это вызвано разными фоновыми процессами исполняемыми на машине в тот момент.
  • Код выглядит следующим образом. Ссылку на весь проект дам ниже.const keys = getKeys();

    performance.mark(‘start’);

    const obj = new createObject(keys);

    performance.mark(‘end’);
    performance.measure(`${length}`, ‘start’, ‘end’);

Результаты

Время на создание одного объекта

Первый, самый главный и простой тест: я взял цикл на 100 итераций. В каждой итерации я создавал новый объект и измерял время на его создание для каждой итерации.

График времени на создание одного объекта в зависимости от итерации

Как видите, во время первого, «холодного» запуска во всех группах создание объекта занимает практически идентичное время. Но уже на второй-третьей итерации скорость выполнения в группах static и mixed (там, где V8 может применить оптимизацию) по сравнению с группой dynamic значительно вырастает.

И это приводит к тому, что благодаря внутренней оптимизации V8 100-й объект из группы static создаётся в 7 раз быстрее.

Или, можно сказать по другому:

не соблюдение порядка объявления свойств в идентичных объектах может привести к семикратному замедлению вашего кода.

Результат не зависит от того как именно вы создаёте объект. Я испытал несколько разных способов и все они дали практически идентичные значения.

const obj = {}
for (let key of keys) obj[key] = 42

// Тоже самое что и первый вариант, но обёрнуто в функцию-конструктов
const obj = new createObject(keys)

const obj = Object.fromEntries(entries)

const obj = eval(‘({ … })’)

Суммарное время создания нескольких объектов

В первом тесте мы обнаружили, что объект из группы dynamic может создаваться на 30-40 микросекунд дольше. Но это только один объект. А в реальных приложениях их могут быть сотни или тысячи. Подсчитаем суммарные накладные расходы в разных масштабах. В следующем тесте я буду последовательно повторять первый, но замерять не время на создание одного объекта, а суммарное время на создание массива таких объектов.

График времени на создание массива объектов в зависимости от его размера

Как видите, в масштабах всего приложения простая внутренняя оптимизация V8 может дать ускорение в десятки раз.

Где это важно

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

При работе с Fetch

fetch(url1, {headers, method, body})
fetch(url2, {method, headers, body})

При работе с jQuery

$.css({ color, margin })
$.css({ margin, color })

При работе с Vue

Vue.component(name1, {template, data, computed})
Vue.component(name2, {data, computed, template})

При работе с паттерном Composite (компоновщик)

const createAlligator = () => ({…canEat(), …canPoop()})
const createDog = () => ({…canPoop(), …canEat()})

И в огромном множестве других мест.

Простое соблюдение порядка свойств в объектах одного типа сделает ваш код более производительным.

Ссылки

Код тестов и подробные результаты
О внутреннем устройстве V8 и оптимизации кода

Источник