Web Components - Custom Elements
Custom Elements
Создание собственных HTML элементов с кастомным поведением.
Примеры пользовательских элементов:
<!-- Использование кастомных элементов -->
<user-rating value="4" max="5"></user-rating>
<collapse-panel title="Важная информация">
<p>Это содержимое будет скрыто/показано при клике на заголовок.</p>
</collapse-panel>
<live-timer format="HH:mm:ss"></live-timer>
<script>
// Кастомный элемент для рейтинга
class UserRating extends HTMLElement {
constructor() {
super();
this.value = parseInt(this.getAttribute("value")) || 0;
this.max = parseInt(this.getAttribute("max")) || 5;
}
connectedCallback() {
this.render();
this.addEventListener("click", this.handleClick.bind(this));
}
render() {
this.innerHTML = `
<div class="rating">
${Array.from({length: this.max}, (_, i) =>
`<span class="star ${i < this.value ? "filled" : ""}" data-value="${i + 1}">★</span>`
).join("")}
</div>
<style>
.rating { display: inline-flex; gap: 2px; }
.star {
cursor: pointer;
font-size: 24px;
color: #ddd;
transition: color 0.2s;
}
.star.filled { color: #ffd700; }
.star:hover { color: #ffed85; }
</style>
`;
}
handleClick(event) {
if (event.target.classList.contains("star")) {
this.value = parseInt(event.target.dataset.value);
this.setAttribute("value", this.value);
this.render();
// Генерация кастомного события
this.dispatchEvent(new CustomEvent("rating-change", {
detail: { value: this.value },
bubbles: true
}));
}
}
}
// Кастомный элемент для сворачиваемой панели
class CollapsePanel extends HTMLElement {
constructor() {
super();
this.isExpanded = this.hasAttribute("expanded");
}
connectedCallback() {
this.render();
}
render() {
const title = this.getAttribute("title") || "Заголовок";
this.innerHTML = `
<div class="collapse-panel">
<div class="header">
<h3>${title}</h3>
<button class="toggle-btn">${this.isExpanded ? "−" : "+"}</button>
</div>
<div class="content" style="display: ${this.isExpanded ? "block" : "none"}">
<slot></slot>
</div>
</div>
<style>
.collapse-panel {
border: 1px solid #ddd;
border-radius: 4px;
margin: 10px 0;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background: #f5f5f5;
cursor: pointer;
}
.header h3 {
margin: 0;
}
.toggle-btn {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.toggle-btn:hover {
background: #e0e0e0;
}
.content {
padding: 15px;
}
</style>
`;
this.querySelector(".header").addEventListener("click", () => {
this.toggle();
});
}
toggle() {
this.isExpanded = !this.isExpanded;
const content = this.querySelector(".content");
const button = this.querySelector(".toggle-btn");
if (this.isExpanded) {
content.style.display = "block";
button.textContent = "−";
this.setAttribute("expanded", "");
} else {
content.style.display = "none";
button.textContent = "+";
this.removeAttribute("expanded");
}
}
}
// Кастомный элемент для живого таймера
class LiveTimer extends HTMLElement {
constructor() {
super();
this.format = this.getAttribute("format") || "HH:mm:ss";
this.interval = null;
}
connectedCallback() {
this.render();
this.start();
}
disconnectedCallback() {
this.stop();
}
render() {
this.innerHTML = `
<div class="live-timer">
<span class="time">${this.getFormattedTime()}</span>
</div>
<style>
.live-timer {
font-family: "Courier New", monospace;
font-size: 18px;
padding: 10px;
background: #2c3e50;
color: #ecf0f1;
border-radius: 4px;
display: inline-block;
}
</style>
`;
}
getFormattedTime() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, "0");
const minutes = now.getMinutes().toString().padStart(2, "0");
const seconds = now.getSeconds().toString().padStart(2, "0");
return this.format
.replace("HH", hours)
.replace("mm", minutes)
.replace("ss", seconds);
}
start() {
this.interval = setInterval(() => {
this.querySelector(".time").textContent = this.getFormattedTime();
}, 1000);
}
stop() {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
}
}
// Регистрация кастомных элементов
customElements.define("user-rating", UserRating);
customElements.define("collapse-panel", CollapsePanel);
customElements.define("live-timer", LiveTimer);
// Обработка событий от кастомных элементов
document.addEventListener("rating-change", (event) => {
console.log("Рейтинг изменен:", event.detail.value);
});
</script>