Created
February 25, 2018 10:39
-
-
Save kazu69/574d6b97ec15212465b468fafb0f9fd8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Document</title> | |
<style> | |
custom-checkbox { | |
vertical-align: middle; | |
} | |
howto-label { | |
vertical-align: middle; | |
display: inline-block; | |
font-weight: bold; | |
font-family: sans-serif; | |
font-size: 20px; | |
margin-left: 8px; | |
} | |
</style> | |
</head> | |
<body> | |
<custom-checkbox id="join-checkbox"></custom-checkbox> | |
<custom-label for="join-checkbox">Join Newsletter</custom-label> | |
<script> | |
(function() { | |
class CustomCheckbox extends HTMLElement { | |
// obsering attributes change | |
// https://developer.mozilla.org/ja/docs/Web/Web_Components/Custom_Elements#Observed_attributes | |
static get observedAttributes() { | |
return ['checked', 'disabled'] | |
} | |
constructor(args) { | |
super(args) | |
this.KEYCODE = { | |
SPACE: 32 | |
} | |
// set initial props | |
this.checked = false | |
this.disabled = false | |
// attach shadowDOM | |
// https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM | |
// https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow | |
const shadowRoot = this.attachShadow({mode: 'open'}) | |
// create <template> tag | |
const template = document.createElement('temaplte') | |
template.innerHTML = this.context | |
this.shadowRoot.appendChild(template) | |
} | |
// set custom element callbacks | |
// https://developer.mozilla.org/ja/docs/Web/Web_Components/Custom_Elements#Custom_element_methods | |
connectedCallback() { | |
console.log(`HowToCheckbox inserted to document`) | |
// set role | |
if (!this.hasAttribute('role')) { | |
this.setAttribute('role', 'checkbox'); | |
} | |
// set tabindex | |
if (!this.hasAttribute('tabindex')) { | |
this.setAttribute('tabindex', 0); | |
} | |
// add eventListner | |
this.addEventListener('keyup', this._onKeyUp) | |
this.addEventListener('click', this._onClick) | |
} | |
get context() { | |
// https://developer.mozilla.org/en-US/docs/Web/CSS/:host | |
return ` | |
<style> | |
:host { | |
box-sizing: border-box; | |
display: inline-block; | |
background-color: #efefef; | |
border: 1px solid #ddd; | |
width: 16px; | |
height: 16px; | |
} | |
:host([hidden]) { | |
display: none; | |
} | |
:host([checked]) { | |
background-color: #000; | |
} | |
:host([disabled]) { | |
background-color: #fff; | |
} | |
:host([checked][disabled]) { | |
background-color: #fff; | |
} | |
</style> | |
` | |
} | |
set checked(value) { | |
const isChecked = Boolean(value); | |
if (isChecked) { | |
this.setAttribute('checked', '') | |
} else { | |
this.removeAttribute('checked') | |
} | |
} | |
get checked() { | |
return this.hasAttribute('checked'); | |
} | |
set disabled(value) { | |
const isDisabled = Boolean(value); | |
if (isDisabled) { | |
this.setAttribute('disabled', '') | |
} else { | |
this.removeAttribute('disabled') | |
} | |
} | |
get disabled() { | |
return this.hasAttribute('disabled'); | |
} | |
_onKeyUp(event) { | |
switch (event.keyCode) { | |
case this.KEYCODE.SPACE: | |
event.preventDefault(); | |
this._toggleChecked(); | |
break | |
default: | |
return | |
} | |
} | |
_onClick(event) { | |
this._toggleChecked() | |
} | |
_toggleChecked() { | |
if (this.disabled) { | |
return | |
} | |
this.checked = !this.checked | |
// dispatch change event | |
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent | |
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent | |
const changeEvent = new CustomEvent({ | |
detail: { | |
checked: this.checked, | |
}, | |
bubbles: true, | |
}) | |
this.dispatchEvent(changeEvent) | |
} | |
disconnectedCallback() { | |
console.log(`HowToCheckbox deleted from document`) | |
this.removeEventListener('keyup', this._onKeyUp) | |
this.removeEventListener('click', this._onClick) | |
} | |
attributeChangedCallback(attributeName, oldValue, newValue) { | |
console.log(`HowToCheckbox attributes changed`) | |
const hasValue = newValue !== null | |
switch(attributeName) { | |
case 'checked': | |
this.setAttribute('aria-checked', hasValue) | |
break | |
case 'disabled': | |
this.setAttribute('aria-disabled', hasValue) | |
if (hasValue) { | |
this.removeAttribute('tabindex') | |
this.blur() | |
} else { | |
this.setAttribute('tabindex', '0') | |
} | |
break | |
} | |
} | |
adoptedCallback() { | |
console.log(`HowToCheckbox adopted from document`) | |
} | |
} | |
// https://developer.mozilla.org/ja/docs/Web/Web_Components/Custom_Elements | |
// https://developer.mozilla.org/ja/docs/Web/API/CustomElementRegistry/define | |
window.customElements.define('custom-checkbox', CustomCheckbox); | |
class CustomLabel extends HTMLElement { | |
constructor() { | |
super() | |
const shadowRoot = this.attachShadow({mode: 'open'}) | |
const template = document.createElement('temaplte') | |
template.innerHTML = this.context | |
this.shadowRoot.appendChild(template) | |
this.addEventListener('click', this._onClick); | |
} | |
static get observedAttributes() { | |
return ['for'] | |
} | |
_onClick(event) { | |
let el = this._currentLabelTarget(); | |
if (!el || event.target === el) { | |
return; | |
} | |
el.focus(); | |
el.click(); | |
} | |
_currentLabelTarget() { | |
let scope = this.getRootNode(); | |
return scope.getElementById(this.for) | |
} | |
connectedCallback() { | |
} | |
get for() { | |
const value = this.getAttribute('for'); | |
return value === null ? '' : value; | |
} | |
set for(value) { | |
this.setAttribute('for', value); | |
} | |
get context() { | |
// <slot> tag https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot | |
return ` | |
<style> | |
:host { | |
cursor: pointer; | |
} | |
</style> | |
<slot></slot> | |
`; | |
} | |
} | |
window.customElements.define('custom-label', CustomLabel); | |
})(); | |
</script> | |
<ul> | |
<li>https://developers.google.com/web/fundamentals/web-components/shadowdom</li> | |
</ul> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://jsfiddle.net/8k7xcgua/1/