Custom elements - garevna/js-course GitHub Wiki
⚠️ Имена кастомных тегов обязательно должны состоять минимум из двух частей, разделенных дефисом, например:
<speaking-club></speaking-club>
<gold-prize></gold-prize>
<mystery-man></mystery-man><protuberance></protuberance>console.dir ( document.querySelector ( 'protuberance' ).__proto__ )► HTMLUnknownElementconsole.dir ( HTMLUnknownElement )ƒ HTMLUnknownElement()
arguments: null
caller: null
length: 0
name: "HTMLUnknownElement"
prototype: HTMLUnknownElement {constructor: ƒ, Symbol(Symbol.toStringTag): "HTMLUnknownElement"}
__proto__: ƒ HTMLElement()Поэтому элемент protuberance унаследует все свойства и методы, которые мы обнаружим в свойстве prototype конструктора HTMLElement
Однако никаких "специфических" свойств и методов у этого элемента не будет, что полностью нивелирует "ценность" подобного творения
⚠️ При создании класса пользовательских элементов в качестве родительского класса ( super ) можно использовать HTMLElement
Тогда создаваемый нами элемент будет наследовать свойства и методы родительского класса HTMLElement.prototype
Свойство customElements ( read-only ) глобального объекта Window содержит ссылку на объект CustomElementRegistry
console.dir ( customElements )▼ CustomElementRegistry
▼ __proto__: CustomElementRegistry
► define: ƒ define()
► get: ƒ ()
► upgrade: ƒ upgrade()
► whenDefined: ƒ whenDefined()
► constructor: ƒ CustomElementRegistry()
Symbol(Symbol.toStringTag): "CustomElementRegistry"
► __proto__: Objectconsole.dir ( CustomElementRegistry )▼ ƒ CustomElementRegistry()
arguments: null
caller: null
length: 0
name: "CustomElementRegistry"
▼ prototype: CustomElementRegistry
► define: ƒ define()
► get: ƒ ()
► upgrade: ƒ upgrade()
► whenDefined: ƒ whenDefined()
► constructor: ƒ CustomElementRegistry()
Symbol(Symbol.toStringTag): "CustomElementRegistry"
► __proto__: Object
► __proto__: ƒ ()Мы будем использовать CustomElementRegistry для регистрации собственных ( кастомных ) элементов
а так же для получения информации об уже зарегистрированных элементах
Метод define() глобального объекта customElements имеет два обязательных параметра:
- первый - это имя тега для регистрируемого элемента
- второй - ссылка на класс создаваемого элемента
customElements.define (
'sample-custom-element',
SampleCustomElement
)☕ 1️⃣
class SampleElement extends HTMLElement {
constructor() {
super ()
let wrapper = document.createElement ( 'div' )
wrapper.className = "wrapper"
this.picture = document.createElement ( 'img' )
this.setPicture ( "https://images.pexels.com/photos/33044/sunflower-sun-summer-yellow.jpg" )
wrapper.appendChild ( this.picture )
this.picture.angle = 0
this.button = document.createElement ( 'button' )
this.button.innerText = 'ROTATE'
this.button.onclick = this.rotatePicture.bind ( this )
wrapper.appendChild ( this.button )
let style = document.createElement ( 'style' )
style.textContent = `
.wrapper {
background-color: #ddddee;
}
img {
width:200px;
margin: 20%;
border: dotted 1px #555;
transition: all 1s;
}
`
this.shadow = this.attachShadow ( { mode: 'open' } )
this.shadow.appendChild ( style )
this.shadow.appendChild ( wrapper )
}
setPicture ( url ) {
this.picture.src = url
}
rotatePicture () {
this.picture.angle += this.picture.angle < 270 ? 90 : -270
this.picture.style.transform = `rotate(${this.picture.angle}deg)`
}
}customElements.define ( 'sample-element', SampleElement )customElements.get ( 'sample-element' )class SampleElement extends HTMLElement {
constructor() {
super ()
let wrapper = document.createElement ( 'div' )
wrapper.className = "wrapper"
this.picture = document.c…let elem = document.createElement ( 'sample-element' )
document.body.appendChild ( elem )☕ 2️⃣
class SampleCustomElement extends HTMLElement {
constructor () {
super ()
let wrapper = document.createElement ( 'div' )
wrapper.className = "wrapper"
this.canvas = document.createElement ( 'canvas' )
wrapper.appendChild ( this.canvas )
this.resizeCanvas ()
this.area = this.canvas.getContext ( "2d" )
this.shadow = this.attachShadow ( { mode: 'open' } )
let style = document.createElement ( 'style' )
style.textContent = `
.wrapper {
background-color: #ddddee;
}
canvas {
border: dotted 1px #555;
}
`
this.shadow.appendChild ( style )
this.shadow.appendChild ( wrapper )
}
resizeCanvas ( event ) {
this.canvas.width = window.innerWidth - 20
this.canvas.height = window.innerHeight - 20
}
drawLine ( first, second, border ) {
this.area.strokeStyle = border && border.lineColor ?
border.lineColor : "#0000ff"
this.area.lineWidth = border && border.lineWidth ?
border.lineWidth : 3
this.area.beginPath()
this.area.moveTo( first.x, first.y )
this.area.lineTo( second.x, second.y )
this.area.stroke()
}
}
customElements.define (
'sample-custom-element',
SampleCustomElement
)
let elem = document.createElement ( 'sample-custom-element' )
document.body.appendChild ( elem )
window.onresize = elem.resizeCanvas.bind ( elem )
elem.drawLine (
{ x:20, y:20 },
{ x:400, y:200 },
{ lineColor: "#008595", lineWidth: 5 }
)☕ 3️⃣
<h3>Пример использования Custom Elements</h3>
<article contenteditable = true>
<p>Статические методы класса объявляются с помощью ключевого слова <b>static</b></p>
<p>Эти методы недоступны из экземпляров класса</p>
<p>Они могут быть вызваны только как методы класса</p>
<words-counter></words-counter>
</article>class WordsCounter extends HTMLElement {
constructor () {
super()
let textContainer = this.parentNode
let shadow = this.attachShadow ( { mode: 'open' } )
let style = document.createElement ( 'style' )
style.textContent = `
span {
background-color: #ddddee;
display: inline-block;
padding: 5px 10px;
color: #578;
border: "1px solid #578";
}
`
shadow.appendChild ( style )
function countWords ( node ) {
var text = node.innerText || node.textContent
return text.split(/\s+/g).length
}
let counter = `Words: ${countWords(textContainer)}`
let counterElem = document.createElement ('span')
counterElem.textContent = counter
shadow.appendChild ( counterElem )
setInterval ( function () {
var counter = `Words: ${countWords(textContainer)}`
counterElem.textContent = counter
}, 200 )
}
}
customElements.define (
'words-counter',
WordsCounter
)☕ 4️⃣
class SampleCustomElement extends HTMLElement {
constructor () {
super ()
let wrapper = document.createElement ( 'div' )
wrapper.className = "wrapper"
this.canvas = document.createElement ( 'canvas' )
wrapper.appendChild ( this.canvas )
this.resizeCanvas ()
this.area = this.canvas.getContext ( "2d" )
this.canvas.self = this
this.canvas.history = []
this.canvas.onmousemove = function ( event ) {
let point = { x: event.clientX, y: event.clientY }
this.self.drawLine (
point,
{ lineColor: "#f0f", lineWidth: 3 } )
}
this.shadow = this.attachShadow ( { mode: 'open' } )
let style = document.createElement ( 'style' )
style.textContent = `
.wrapper {
background-color: #ddddee;
}
canvas {
border: dotted 1px #555;
}
`
this.shadow.appendChild ( style )
this.shadow.appendChild ( wrapper )
}
resizeCanvas ( event ) {
this.canvas.width = window.innerWidth - 20
this.canvas.height = window.innerHeight - 20
}
drawLine ( point, border ) {
if ( !point || !point.x || !point.y ) return
this.canvas.history.push ( point )
let len = this.canvas.history.length
if ( len < 2 ) return
let prev = this.canvas.history [ len - 2 ]
this.area.strokeStyle = border && border.lineColor ?
border.lineColor : "#0000ff"
this.area.lineWidth = border && border.lineWidth ?
border.lineWidth : 3
this.area.beginPath()
this.area.moveTo( prev.x, prev.y )
this.area.lineTo( point.x, point.y )
this.area.stroke()
}
}
customElements.define (
'sample-custom-element',
SampleCustomElement
)
let elem = document.createElement (
'sample-custom-element'
)
document.body.appendChild ( elem )
window.onresize = elem.resizeCanvas.bind ( elem )