Created
October 12, 2015 12:38
-
-
Save kwltrs/bb7fccb70a774d991baa to your computer and use it in GitHub Desktop.
Code extracted from my talk "Fancy Input Elements with Web Components" held at Fronteers 2015 Jam Session. https://fronteers.nl/congres/2015/jam-session
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> | |
<head> | |
<meta charset=utf-8> | |
<link rel=import href=star-rating.html> | |
<style>input[type=range] { font-size: 3em; } output { font-size: 2em; padding-left: 2em; }</style> | |
</head> | |
<body> | |
<form> | |
<input id=input type=range is=star-rating> | |
<output id=aout></output> | |
</form> | |
<script> | |
(function () { | |
'use strict'; | |
const ratings = ['I hate it', 'I don\'t like it', 'It\'s okay', 'I like it', 'I love it']; | |
const rating = document.getElementById('input'); | |
const aout = document.getElementById('aout'); | |
rating.addEventListener('change', (evt) => { | |
let v = rating.value, s = ratings[v-1]; | |
aout.value = `${s} (${v} stars)`; | |
}); | |
}()); | |
</script> | |
<a class="shameless-plug" href="http://www.knowit.no/systemutviklere/">We're hiring</a> | |
</body> | |
</html> |
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
<template id=star-rating-template> | |
<style> | |
:host { | |
-webkit-appearance: none; | |
color: inherit; background-color: inherit; | |
} | |
:host(:focus) { outline: none; } | |
.star { color: papayawhip; text-shadow: 0 0 1px indigo; cursor: pointer; } | |
.star.selected { color: orange; } | |
.stars:hover .star { color: lightskyblue; } | |
.star:hover ~ .star { color: papayawhip; } | |
.star:hover ~ .star.selected { color: orange; } | |
</style> | |
<span class=stars> | |
<span class=star>★</span><span class=star>★</span><span class=star>★</span><span class=star>★</span><span class=star>★</span> | |
<span> | |
</template> | |
<script> | |
(function () { | |
'use strict'; | |
const toArray = (_) => Array.prototype.slice.call(_); | |
const markSelected = (el) => el.classList.add('selected'); | |
const markDeselected = (el) => el.classList.remove('selected'); | |
const importDoc = document.currentScript.ownerDocument; | |
const template = importDoc.querySelector('#star-rating-template'); | |
document.registerElement('star-rating', { | |
extends: 'input', | |
prototype: Object.create(HTMLInputElement.prototype, { | |
createdCallback: { | |
value: function () { | |
this.min = 0; this.max = 5; this.step = 1; | |
this.value = this.attributes.value || 0; | |
const root = this.createShadowRoot(); | |
root.appendChild( template.content.cloneNode(true) ); | |
const stars = toArray( root.querySelectorAll('.star') ); | |
const ratingOfStar = (el) => stars.indexOf(el) + 1; | |
const isStarElement = (el) => stars.indexOf(el) !== -1; | |
const highlightStar = (value) => { | |
stars.slice(0, value).forEach( markSelected ); | |
stars.slice(value).forEach( markDeselected ) | |
}; | |
const setRating = (newValue) => { | |
highlightStar( newValue ); | |
if (this.value != newValue) { | |
this.value = newValue; | |
this.dispatchEvent( new Event('change') ); | |
} | |
}; | |
this.addEventListener('click', (evt) => { | |
let el = evt.path[0]; | |
if ( isStarElement(el) ) { | |
setRating( ratingOfStar(el) ); | |
} | |
}); | |
setRating( this.value ); | |
} | |
} | |
}) | |
}); | |
}()); | |
</script> |
Hey, I was working on implementing the same thing and ran into the multiple shadow root problem too. Did you ever figure out how to work around that?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The code works on chromium 45. As I mentioned during my talk, calling
Element.createShadowRoot()
for an element which already hosts a user-agent root is deprecated. So the approach I showed here is not going to work in future.Please leave a comment or get in touch if you know a better approach to implement a widget behaving like a native
HTMLInputElement
, e.g. will be included inform.elements
orFormData.get
, without adding an hidden input field to the host document.Me: https://www.npmjs.com/package/@kwltrs/about-me