По умолчанию 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>