<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Sample Mixin-Based Component</title>
<!-- See "CSS" tab for styles -->
<script>
// This sample demonstrates how component behavior may be factored into
// separate mixins. In this sample, three separate mixins handle input,
// abstract semantics, and output, respectively. The three can be combined
// to produce give a custom element very rudimentary selection support.
//
// These particular mixins are overly simplistic in order to focus just on
// the way the mixins communicate. Real implementations of these would be
// more complex in order to handle Shadow DOM distribution, events, issues
// in older browsers, etc.
//
// This sample currently requires Google Chrome or Safari Technology Preview.
// Simplistic mixin mapping click input to selection semantics.
const ClickSelectionMixin = (base) => class ClickSelection extends base {
constructor() {
super();
// When a child is clicked, set the selectedItem.
this.addEventListener('click', event => {
this.selectedItem = event.target;
event.stopPropagation();
});
}
}
// Simplistic mixin for single selection.
// This can remember which of the component's children is selected.
// This also maps component selection semantics (which item is selected)
// to item selection semantics (is an individual item selected or not).
const selectedItemSymbol = Symbol('selectedItem');
const SingleSelectionMixin = (base) => class SingleSelection extends base {
// Turn the selection state of an individual item on or off.
// The exact semantics of that can be defined elsewhere.
// Note: To simplify inspection of this sample in the debugger, this
// method has a string name. In practice, this internal method would
// be referenced via a Symbol key instead to avoid exposing it in the
// component's public API.
applySelection(item, selected) {
if (super.applySelection) { super.applySelection(item, selected); }
// By default, nothing happens when something is selected.
}
// The item (child) which is currently selected.
get selectedItem() {
return this[selectedItemSymbol];
}
set selectedItem(item) {
// Remember which item has now become selected.
const previousSelectedItem = this[selectedItemSymbol];
this[selectedItemSymbol] = item;
// Invoke super in case other mixins implement this property.
// Note: we detect whether the property is defined further up the
// prototype chain by inspecting `in base.prototype`, since `in super`
// is not valid, and just accessing `super.selectedItem` would actually
// return the current value of that base class property if it existed.
if ('selectedItem' in base.prototype) { super.selectedItem = item; }
if (previousSelectedItem !== item) {
// The selected item changed.
if (previousSelectedItem) {
// Remove selection from previous item.
this.applySelection(previousSelectedItem, false);
}
// Apply selection to new item.
this.applySelection(item, true);
}
}
}
// Simplistic mixin mapping item selection to a `selected` CSS class.
const SelectionClassMixin = (base) => class SelectionClass extends base {
applySelection(item, selected) {
if (super.applySelection) { super.applySelection(item, selected); }
item.classList.toggle('selected', selected);
}
}
// Define a custom element using the above mixins.
// Significantly, the order of mixin application doesn't matter.
class TestElement extends
ClickSelectionMixin(SelectionClassMixin(SingleSelectionMixin(HTMLElement))) {}
customElements.define('test-element', TestElement);
</script>
</head>
<body>
<p>
Click an item to select it:
</p>
<test-element>
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
</test-element>
</body>
</html>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 15px;
margin: 0;
padding: 25px;
}
test-element {
cursor: default;
user-select: none;
user-select: none;
user-select: none;
user-select: none;
}
test-element > * {
align-items: center;
border: 1px solid lightgray;
display: inline-flex;
height: 50px;
justify-content: center;
padding: 0.25em;
width: 50px;
}
test-element > .selected {
background-color: highlight;
color: highlighttext;
}
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |