Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
<!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>
@kazu69
Copy link
Author

kazu69 commented Feb 25, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment