커스텀엘리먼트는 개발자가 인터페이스로 정의하는 플랫폼 객체이다. 이것을 custom element prototype
이라 부른다.(w3c spec)
모든 웹 개발자들이 새로운 타입의 HTML element를 정의하는 것.
- 새 HTML/DOM element 정의하기
- 다른 Element부터 확장된 element 만들기
- 하나의 태그에 사용자 지정 기능을 함께 논리적으로 제공하기
- 존재하는 DOM element의 API 확장하기
단순하게 생각해서 html
> body
에 <sometag></sometag>
를 추가하면 에러가 발생하지 않는다.
그럼 단순하게 태그만 추가하는 것으로 custom element를 만든것일까??
아래의 콘솔을 보자
위에서 보듯이 <sometag></sometag>
는 HTMLUnkownElement
를 상속받는다.
<div></div>
태그는?
오.. HTMLDivElement
를 상속받았다.
이렇듯 현재 spec에 정의되어있는 엘리먼트들은 HTMLElement
를 상속받지만
정의되지않는 <sometag></sometag>
를 적으면 HTMLUnknownElement
를 상속받고 에러는 발생하지않는다.
유효한 이름을 갖는 Element는 HTMLElement를 상속받는다.
유효하지 않는 이름을 갖는 element는 HTMLUnknownElement
로 부터 상속된다.
잘 상속 받았는지 확인하는 소스
document.createElement('tabs').__proto__ === HTMLUnknownElement.prototype
document.createElement('tabs').__proto__ == HTMLElement.prototype
W3C스팩에 보면 기존엘리먼트와 커스텀엘리먼트를 구별하기위해 Dash(-)가 포함되어있고 소문자인 명명규칙이있다. 그리고 몇개의 예약어들도 명명해서는 안된다 라고 나와있다. 여기에
그렇다면 Dash와 소문자로 만들어진 '그냥 단지(<just-dash></just-dash>
)' 태그를 만들어서 마크업해보자
오잉. HTMLElement
를 상속받았다. 그럼 이제 커스텀엘리먼트를 다 만든걸까??
<x-tabs>
을 페이지 상에 선언할 수 있지만document.registerElement('x-tabs');
호출 이후 훨씬 나중에 종료할 수 있다. elements가 그들의 정의로 업그레이드 되기전에 그들은 unresolved elements로 불린다. 유효한 custom element이름을 갖지만 등록되지 않은 HTML element이다.
- 출처 : html5rocks
라고 html5rocks에서 말하고 있는데 쉽게 생각하면 Custom Element 의 명명규칙을 따르지만, register 되지않은 상태를 말한다. 아래의 표는 그 둘의 차이점을 보여준다.
이름 | 상속받은 parent | 예 |
---|---|---|
Unresolved element | HTMLElement | <x-tabs> , <my-element> , <my-awesome-app> |
Unknown element | HTMLUnknownElement | <tabs> , <foo_bar> |
registed된 엘리먼트는 자기자신을 constructor를 갖지만 Unresolved 엘리먼트는 HTMLElement
를 constructor로 갖는다. - stackoverflow에서...
document.registerElement('amos-forward');
document.createElement('amos-forward').constructor
//⇒ function amos-forward() { [native code] }
document.createElement('amos-forward-not-registered').constructor
//⇒ function HTMLElement() { [native code] }
-
첫번째 인자 : String, 작성할 Custom Element명 -> 반드시 Dash(-) 포함
이는 parser가 일반 element들로부터 custom element를 구별할 수 있도록 하며 HTML에 새태그가 추가될때 앞으로의 호환성을 보장.
ex)
<x-tag>
,<my-ele>
은 유효한 이름이며,<tabs>
,<foo_bar>
는 유효하지 않음
-
두번째 인자 : Object, prototype정보
아래 두문장은 같음.
document.registerElement('amos-forward');
document.registerElement('amos-forward', {
prototype: Object.create(HTMLElement.prototype)
});
var AmosMidfielder = document.registerElement('amos-midflieder');
document.body.appendChild(new AmosMidfielder());
변수를 소문자?? or 대문자??
var amosMidfielder
? orvar AmosMidfielder
??
prototype을 전달해 준다.
var AmosButton = document.registerElement('amos-btn', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
확장된 요소들은 type extension custom elements라 불리우며, "element X is a Y"와 같이 사용
<button is="amos-btn">amos Button</button>
HTML5Rocks.com에서 custom element도 상속을 해서 새로운 custom element를 만들수 있다고 하지만 오류가 발생함.
var AmosPlayer = document.registerElement('amos-player');
var amosPlayer = new AmosPlayer();
var AmosForward = document.registerElement('amos-forward', {
prototype: amosPlayer.prototype,
extends: 'amos-player'
})
// Uncaught DOMException: Failed to execute 'registerElement' on 'Document': Registration failed for type 'amos-forward'. The tag name specified in 'extends' is a custom element name. Use inheritance instead.(…)
근데 요것을 해결 할 수 있는 방법이 있음
//HTMLElement의 프로토타입을 상속받는 객체를 만들고
var amosPlayerProto = Object.create(HTMLElement.prototype);
//method 정의
amosPlayerProto.kick = function () {
return 'kick~~';
}
// 읽기전용 속성 정의
Object.defineProperty(amosPlayerProto, 'HomeColor', {value: 'red'});
//커스텀 엘리먼트를 등록할때 프로토타입을 설정한다.
var AmosPlayer = document.registerElement('amos-player', {
prototype: amosPlayerProto
});
//상속받을 엘리먼트의 프로토타입을 AmosPlayer의 프로토타입을 상속받는다.(HTMLElement <- AmosPlayer)
var amosForwardProto = Object.create(AmosPlayer.prototype);
amosForwardProto.attack = function () {
return 'attack!!!!!!';
}
var AmosForward = document.registerElement('amos-forward', {
prototype: amosForwardProto
})
기존spec에 존재하는 엘리먼트를 extends하면 잘된다. 근데 custom으로 한것을 extends 설정을 해주면 에러가 발생한다... 안해주면 안발생하고 상속도 잘됨.. 왜그럴까???
위의 상속예제에서 보듯이 메서드와 속성을 추가할수 있고 다른 방법하나 더 추가
var AmosGK = document.registerElement('amos-gk', {
prototype: Object.create(HTMLElement.prototype, {
keep: {
header: function () { return '머리로 막아~~'; }
},
punching: {
value: function () {
alert('펀칭 해버려~~~');
}
}
})
});
위의 코드가 가독성이 더 좋아보인다.
현재 ie, ff, safari를 지원하지 않는것이 결정적임. 확인은 여기서
콜백 | 발생지점 |
---|---|
createdCallback | 인스턴스가 생성될 때 |
attachedCallback | 생성된 인스턴스가 문서에 추가될때 |
detachedCallback | 인스턴스가 문서에서 제거될 때 |
attributeChangedCallback(attrName, oldVal, newVal) | 속석이 변경될 때 (추가/삭제/수정) |
var amosMidfielder = Object.create(HTMLElement.prototype);
amosMidfielder.createdCallback = function () { return console.log('create~~~~~'); }
amosMidfielder.attachedCallback = function () { return console.log('attached~~~~'); }
amosMidfielder.detachedCallback = function () { return console.log('detached~~~~'); }
amosMidfielder.attributeChangedCallback = function () { return console.log('change~~~~'); }
var AmosMidfielder = document.registerElement('amos-mid', {
prototype: amosMidfielder
})
특이한점은 등록되는 객체 AmosMifielder
에 콜백펑션들이 붙이않고 prototype
붙는다는 것이다.
인스턴스화 하는 법 Referrence 참고
- W3C 스팩은 어떻게 읽어야 하는가?
- W3C Spec
- html5rocks 원문
- html5rocks 번역
- W3C 한글번역 대한민국 사무국
- Custom Elements 발표 by 카나프
- NCName이란 - MDSN 파생 XML데이터 형식(영문)
- 상속받을때 new가 아닌
Object.create()
하는 이유 - https://customelements.io/
- A Detailed Introduction To Custom Elements : 튜토리얼 스매싱매거진(영문)
- How to Create Custom HTML Elements : 튜토리얼 (영문)
- document.registerElement() : MDN(영문)
document.reigsterElement()
의 브라우저 지원을 나타내주고 있다. - caniuse : 브라우저별 지원을 쉽게 찾을 수 있다. (영문)
- WEB COMPONENTS & POLYMER - NAVER 박재성
custom element browser support
MDN
http://caniuse.com/#feat=custom-elements
Safari, I.E., FireFox 지원 안함..