По умолчанию HTML предоставляет ряд встроенных элементов, из которых мы можем составить структуру веб-страницы. Однако мы не ограничены встроенными html-элементами и можем сами создать и использовать свои элементы html.
В JavaScript HTML-элемент представлен интерфейсом HTMLElement. Соответственно, реализуя данный интерфейс в JavaScript, мы можем создать свои классы, которые будут представлять элементы html, и потом их использовать. Что-то наподобие следующего:
<!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8"> </head> <body> <hello-metanit></hello-metanit> </script> </body> </html>
В данном случае в коде странице определен элемент <hello-metanit>
, и в реальности такого элемента конечно же не существует.
Но сейчас мы его создадим.
Итак, чтобы определить класс, который будет представлять html-элемент, нам достаточно создать класс, который реализует интерфейс HTMLElement:
class HelloMetanit extends HTMLElement { }
Второй важный момент - нам надо зарегистрировать наш кастомный html-элемент, что бы браузер знал, что есть такой элемент. Для этого применяется встроенная функция
customElements.define(name, constructor, options);
Она принимает три параметра:
name
: имя кастомного элемента html, который будет представлять класс JavaScript. Важно: имя должно содержать дефис.
constructor
: конструктор (по сути класс JavaScript), который представляет кастомный элемент html.
options
: необязательный параметр - объект, который настраивает кастомный html-элемент. В настоящий момент он поддерживает
один параметр - extends
. Он определяет название встроенного html-элемента, который применяется для создания кастомного элемента html.
Например, в нашем случае мы могли бы вызвать эту функцию так:
customElements.define("hello-metanit", HelloMetanit);
То есть в общем это будет выглядеть следующим образом:
<!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8"> </head> <body> <hello-metanit></hello-metanit> <script> class HelloMetanit extends HTMLElement { } customElements.define("hello-metanit", HelloMetanit); </script> </body> </html>
Но пока кастомный элемент "hello-metanit" ничего не делает. Добавим ему какую-нибудь примитивную задачу. Пусть он выводит некоторое приветствие.
Как правило, классы кастомных элементов применяют конструктор. Причем в самом начале конструктора должен идти вызов функции
super(), который гарантирует, что наш класс унаследовал все методы, атрибуты и свойства интерфейса
HTMlElement
.
class HelloMetanit extends HTMLElement { constructor() { super(); } }
Но кроме того, в конструкторе мы можем определить некоторую базовую логику нашего элемента. Например:
<!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8"> </head> <body> <hello-metanit></hello-metanit> <script> class HelloMetanit extends HTMLElement { constructor() { super(); let welcome = "Доброе утро"; const hour = new Date().getHours(); if (hour > 17) { welcome = "Добрый вечер"; } else if (hour > 12) { welcome = "Добрый день"; } this.innerText= welcome; // либо так // this.textContent = welcome; } } customElements.define("hello-metanit", HelloMetanit); </script> </body> </html>
В конструкторе мы получаем текущее время и в зависимости от текущего часа определяем текст приветствия. Поскольку наш класс применяет интерфейс
HTMLElement, то соответственно мы можем в нем использовать стандартные для html-элементов свойства. В частности, в данном случае
для установки текста элемента применяется свойство innerText
(также можно было бы использовать свойство textContent
).
Как и в обычных классах, мы можем определять в классах элементов методы и затем вызывать их. Например, определим простейший метод, который возвращает текущее время:
<!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8"> </head> <body> <hello-metanit id="hello"></hello-metanit> <script> class HelloMetanit extends HTMLElement { constructor() { super(); let welcome = "Доброе утро"; const hour = new Date().getHours(); if (hour > 17) { welcome = "Добрый вечер"; } else if (hour > 12) { welcome = "Добрый день"; } this.style="cursor:pointer;" this.innerText= welcome; } showTime(){ console.log(new Date().toTimeString()); } } customElements.define("hello-metanit", HelloMetanit); // получаем элемент const hello = document.getElementById("hello"); // по нажатию вызываем его метод showTime hello.addEventListener("click", ()=> hello.showTime()); </script> </body> </html>
Для примера в классе элемента определен метод showTime, который просто выводит на консоль текущее время. В коде javascript мы получаем по
id данный элемент, прикрепляем к нему обработчик нажатия, в котором вызываем вышеопределенный метод showTime()
.
В итоге по нажатию в консоли мы увидим текущее время:
Кастомный элемент html имеет свой жизненный цикл, который описывается следующими методами:
connectedCallback: вызывается каждый раз, когда кастомный элемент html добавляется в DOM.
disconnectedCallback: вызывается каждый раз, когда кастомный элемент html удаляется из DOM.
adoptedCallback: вызывается каждый раз, когда кастомный элемент html перемещается в новый элемент.
attributeChangedCallback: вызывается при каждом изменении (добавлении, изменении значения или удаления) атрибута кастомного элемента html.
Например, применим метод connectedCallback()
:
<!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8"> </head> <body> <hello-metanit id="hello"></hello-metanit> <script> class HelloMetanit extends HTMLElement { constructor() { super(); let welcome = "Доброе утро"; const hour = new Date().getHours(); if (hour > 17) { welcome = "Добрый вечер"; } else if (hour > 12) { welcome = "Добрый день"; } this.style.cursor="pointer" this.innerText= welcome; } connectedCallback() { this.style.color = "red"; } showTime(){ console.log(new Date().toTimeString()); } } customElements.define("hello-metanit", HelloMetanit); </script> </body> </html>
В данном случае в методе connectedCallback()
просто устанавливаем цвет шрифта - в данном случае красный цвет:
this.style.color = "red";
Также мы можем определить у элемента свои атрибуты и затем использовать их. Например, выше при добавлении элемента на страницу у него устанавливается красный цвет текста. Зададим установку цвета с помощью атрибута:
<!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8"> </head> <body> <hello-metanit hellocolor="#2980b9"></hello-metanit> <br/> <hello-metanit></hello-metanit> <script> class HelloMetanit extends HTMLElement { constructor() { super(); let welcome = "Доброе утро"; const hour = new Date().getHours(); if (hour > 17) { welcome = "Добрый вечер"; } else if (hour > 12) { welcome = "Добрый день"; } this.style.cursor="pointer" this.innerText= welcome; } connectedCallback() { this.style.color = "red"; if (this.hasAttribute("hellocolor")) { this.style.color = this.getAttribute("hellocolor"); } } showTime(){ console.log(new Date().toTimeString()); } } customElements.define("hello-metanit", HelloMetanit); </script> </body> </html>
В данном случае элемент принимает атрибут hellocolor
, который задает цвет текста элемента. Если этот атрибут определен, то по нему устанавливаем
цвет текста. Если не определен, то применяется цвет по умолчанию - красный:
this.style.color = "red"; if (this.hasAttribute("hellocolor")) { this.style.color = this.getAttribute("hellocolor"); }
Стилизация элемента через CSS производится также, как и стилизация любого другого элемента:
<!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8"> <style> hello-metanit{ font-family: Verdana; font-size:22px; } </style> </head> <body> <hello-metanit hellocolor="#2980b9"></hello-metanit> <script> class HelloMetanit extends HTMLElement { constructor() { super(); let welcome = "Доброе утро"; const hour = new Date().getHours(); if (hour > 17) { welcome = "Добрый вечер"; } else if (hour > 12) { welcome = "Добрый день"; } this.style.cursor="pointer" this.innerText= welcome; } connectedCallback() { this.style.color = "red"; if (this.hasAttribute("hellocolor")) { this.style.color = this.getAttribute("hellocolor"); } } showTime(){ console.log(new Date().toTimeString()); } } customElements.define("hello-metanit", HelloMetanit); </script> </body> </html>