var doc_tests = document.getElementById("document_tests");
function doc_test() {
var doc = document;
doc_tests.innerHTML =
"doc.documentElement: " + doc.documentElement +
"doc.defaultView: " + doc.defaultView +
"doc.activeElement: " + doc.activeElement +
"doc.scrollingElement: " + doc.scrollingElement +
"doc.head: " + doc.head +
"doc.contentType: " + doc.contentType +
"doc.implementation: " + doc.implementation +
"doc.inputEncoding: " + doc.inputEncoding +
"doc.characterSet: " + doc.characterSet +
"doc.domain: " + doc.domain +
"doc.location: " + doc.location +
"doc.baseURI: " + doc.baseURI +
"doc.URL: " + doc.URL +
"doc.referrer: " + doc.referrer +
"doc.readyState: " + doc.readyState +
"doc.lastModified: " + doc.lastModified +
"doc.documentMode: " + doc.documentMode +
"doc.compatMode: " + doc.compatMode +
"doc.cookie: " + doc.cookie +
"doc.visibilityState: " + doc.visibilityState +
"doc.hidden: " + doc.hidden +
"doc.fullscreenEnabled: " + doc.fullscreenEnabled +
"doc.fullscreenElement: " + doc.fullscreenElement +
"doc.title: " + doc.title +
"doc.body: " + doc.body +
"doc.hasFocus(): " + doc.hasFocus() +
"doc.documentURI: " + doc.documentURI +
"doc.strictErrorChecking: " + doc.strictErrorChecking +
"doc.scripts: " + doc.scripts +
"doc.currentScript: " + doc.currentScript +
"doc.images: " + doc.images +
"doc.links: " + doc.links +
"doc.anchors: " + doc.anchors +
"doc.forms: " + doc.forms +
"doc.applets: " + doc.applets +
"doc.embeds: " + doc.embeds +
"doc.plugins: " + doc.plugins +
"doc.fonts: " + doc.fonts +
"doc.styleSheets: " + doc.styleSheets +
"doc.designMode: " + doc.designMode;
}
setInterval(doc_test,500);
function el_info_test() {
var el_it = document.getElementById("el_info_test");
el_it.innerHTML =
"el_it.tagName: " + el_it.tagName +
"el_it.nodeName: " + el_it.nodeName +
"el_it.nodeType: " + el_it.nodeType +
"el_it.nodeValue: " + el_it.nodeValue +
"el_it.offsetTop: " + el_it.offsetTop +
"el_it.offsetLeft: " + el_it.offsetLeft +
"el_it.offsetWidth: " + el_it.offsetWidth +
"el_it.offsetHeight: " + el_it.offsetHeight +
"el_it.offsetParent: " + el_it.offsetParent +
"el_it.getBoundingClientRect: " + el_it.getBoundingClientRect +
"el_it.ownerDocument: " + el_it.ownerDocument +
"el_it.clientTop: " + el_it.clientTop +
"el_it.clientLeft: " + el_it.clientLeft +
"el_it.clientWidth: " + el_it.clientWidth +
"el_it.clientHeight: " + el_it.clientHeight +
"el_it.scrollWidth: " + el_it.scrollWidth +
"el_it.scrollHeight: " + el_it.scrollHeight;
}
el_info_test();
<div
id="el_attributes_test"
class="example h-25"
title="Attributes TEST"
dir="ltr"
lang="en"
tabindex="132"
onclick = "console.log('Dont click ME !')"
onmouseover = "this.style.background = 'lightgray'"
onmouseout = "this.style.background = 'white'"
contenteditable
>Tests DIV</div>
var el_at = document.getElementById("el_attributes_test");
function el_attributes_test() {
var attr_thing = document.createAttribute("data-thing-two");
attr_thing.value = "thing value two";
el_at.innerHTML = "Array.from(el_at.attributes).map((e)=>(e.name+':'+e.value)):" +
Array.from(el_at.attributes).map(
(e)=>(e.name+" : "+e.value + " , specified:" + (e.specified ? "yes":"no")) ) +
"el_at.attributes.getNamedItem('onclick').value: " +
el_at.attributes.getNamedItem('onclick').value +
"el_at.hasAttributes(): " + el_at.hasAttributes() +
"el_at.hasAttribute('id'): " + el_at.hasAttribute('id') +
"el_at.hasAttribute('data-thing'): " + el_at.hasAttribute('data-thing') +
"el_at.getAttribute('id'): " + el_at.getAttribute('id') +
"el_at.setAttribute('data-thing', 'thing value')" +
"el_at.getAttribute('data-thing'): " + el_at.getAttribute('data-thing') +
"el_at.setAttributeNode(attr_thing): " + el_at.setAttributeNode(attr_thing) +
"el_at.getAttributeNode('data-thing-two'): " + el_at.getAttributeNode('data-thing-two') +
"el_at.removeAttribute('data-thing')" +
"el_at.hasAttribute('data-thing'): " + el_at.hasAttribute('data-thing') +
"el_at.removeAttributeNode(attr_thing): " +
el_at.removeAttributeNode(attr_thing).name + // .value
"el_at.id: " + el_at.id +
"el_at.title: " + el_at.title +
"el_at.lang: " + el_at.lang +
"el_at.dir: " + el_at.dir +
"el_at.tabIndex: " + el_at.tabIndex;
}
el_attributes_test();
el_at.attributes.removeNamedItem("onmouseout");
var onmouseout = document.createAttribute("onmouseout");
onmouseout.value = "this.style.background = 'white'";
el_at.attributes.setNamedItem(onmouseout);
var el_find = document.getElementById("el_find_test");
el_find.accessKey = "e";
var d = document;
el_find.innerHTML =
"d.querySelector('#el_find_test').innerHTML: " +
d.querySelector('#el_find_test').innerHTML +
d.querySelectorAll('h3')[1].innerHTML +
d.getElementsByTagName('h2')[1].id +
d.getElementsByClassName('find_test_class')[0].id +
d.getElementById('document').nextSibling.nextSibling.tagName +
d.getElementById('element').nextElementSibling.nextElementSibling.tagName +
d.getElementById('el_find_test').previousSibling.previousSibling.tagName +
d.getElementById('el_find_test').previousElementSibling.tagName
"el_find.accessKey: " + el_find.accessKey;
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT,
{
acceptNode: function(node) {
if ( /^H2*$/.test(node.tagName) ) {
// if ( ! /^\s*$/.test(node.data) ) {
return NodeFilter.FILTER_ACCEPT;
}
}
},
false
);
// avoid changing DOM inside NodeFilter, make it here ...
while(treeWalker.nextNode()) {
el_find.innerHTML += "treeWalker.currentNode.id: " + treeWalker.currentNode.id;
}
// OR
// var node;
// while ((node = iterator.nextNode())) { alert(node.data); }
var nodeIterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_ELEMENT,
function(node) {
return node.nodeName === 'H3' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
);
var currentNode;
while (currentNode = nodeIterator.nextNode()) {
el_find.innerHTML += "nodeIterator.nextNode() innerHTML: " + currentNode.innerHTML;
}
function el_style_test() {
var el_style = document.getElementById("el_style_test");
// var d = document;
el_style.style.color = "fireBrick";
el_style.innerHTML =
"SET el_style.style.color = 'fireBrick'" +
"el_style.style.color: " + el_style.style.color +
"el_style.classList: " + el_style.classList +
"el_style.className: " + el_style.className;
el_style.className = "example";
el_style.innerHTML +=
"SET el_style.className = 'example'" +
"el_style.classList: " + el_style.classList +
"el_style.className: " + el_style.className;
}
el_style_test();
var check_tests = document.getElementById('check_tests');
var check_tests_result = document.getElementById('check_tests_result');
var list_1 = document.getElementById('myList1');
list_1.contentEditable = true;
var list_1_children = list_1.children;
var list_2 = document.getElementById('myList2');
var list_2_children = list_2.children;
var list_3 = document.getElementById('myList3');
var list_3_children = list_3.children;
check_tests_result.innerHTML =
list_1.contains(list_1_children[0]) +
list_1.contains(list_2_children[0]) +
list_1_children[0].isEqualNode(list_3_children[0]) +
list_1_children[0].isEqualNode(list_2_children[0]) +
list_1_children[0].isSameNode(list_1_children[0]) +
list_1_children[0].isSameNode(list_3_children[0]) +
list_1.compareDocumentPosition(list_1_children[0]) +
list_2.compareDocumentPosition(list_3) +
list_1.isContentEditable +
list_3_children[1].parentNode.id +
list_1_children[1].parentElement.id +
list_1.childElementCount +
list_1.firstChild.innerHTML +
list_2.lastChild.innerHTML +
list_3.firstElementChild.innerHTML +
list_3.lastElementChild.innerHTML;
// append new child to list_1
var li_node = document.createElement("LI");
var li_text = document.createTextNode("Juice");
li_node.appendChild(li_text);
list_1.appendChild(li_node);
// then move it to the end of list_2 - appendChild
var node = list_1.lastChild;
list_2.appendChild(node);
// then make it first in list_3 - insertBefore
list_3.insertBefore(list_2.lastChild, list_3.childNodes[0])
// remove and append same list item
var item = document.getElementById("the_vodka");
function removeLi() { item.parentNode.removeChild(item); }
function appendLi() {
list_3.appendChild(item);
}
// remove list item only by its ID
// var item = document.getElementById("the_vodka");
// item.parentNode.removeChild(item);
// If the ul element has any child nodes, remove its first child node
var list_4 = document.getElementById("myList4");
if (list_4.hasChildNodes()) {
// As long as ul has a child node, remove it
while (list_4.hasChildNodes()) {
list_4.removeChild(list_4.firstChild);
}
}
// replace
check_tests.replaceChild(
document.createTextNode("No More List..."),
check_tests.children[3]
);
// remove a span element from its parent and insert it to an h1 element in another document
// var child = document.getElementById("mySpan");
// function removeLi() { child.parentNode.removeChild(child);
// function myFunction() {
// var frame = document.getElementsByTagName("IFRAME")[0]
// var h = frame.contentWindow.document.getElementsByTagName("H1")[0];
// var x = document.adoptNode(child);
// h.appendChild(x);
// }
// document.documentElement.isDefaultNamespace("http://www.w3.org/1999/xhtml")
var el_event_test = document.getElementById("el_event_test");
var the_clicks = 0;
var clicks_inc = (e) => {e.target.value = ++the_clicks};
function event_focus () { el_event_test.focus(); }
function event_blur () { el_event_test.blur(); }
function event_click () { el_event_test.click(); }
function event_add () {
el_event_test.addEventListener("click", clicks_inc);
el_event_test.addEventListener("mousemove", clicks_inc);
el_event_test.addEventListener("touchmove", clicks_inc);
}
function event_remove () {
el_event_test.removeEventListener("click", clicks_inc);
el_event_test.removeEventListener("mousemove", clicks_inc);
el_event_test.removeEventListener("touchmove", clicks_inc);
}
<div id="scroll_test" class="example w-50 h-10" style="overflow: scroll; white-space: nowrap;">
--------------------1--- ...
</div>
function scroll_into(){
document.getElementById("scroll_btn").scrollIntoView();
}
function scroll_top(){
document.getElementById("scroll_test").scrollTop += 10;
}
function scroll_left(){
document.getElementById("scroll_test").scrollLeft += 10;
}
var ct = document.getElementById("coll_tests"),
headers = document.getElementsByTagName("H2");
ct.innerHTML = "header tags count: " + headers.length + "IDs:" +
Array.from(headers).map((e)=>(e.id)) +
"headers.namedItem('collection').offsetTop: " +
headers.namedItem("collection").offsetTop;
<button onclick="eventTests(event)">Try it</button>
var for_event_div = document.getElementById("for_event_div");
for_event_div.addEventListener("click", clickTests);
function clickTests(e) {
e.preventDefault();
e.stopImmediatePropagation();
e.stopPropagation();
e.cancelBubble = true;
var for_event = document.getElementById("for_event");
var for_event_result = document.getElementById("for_event_result");
for_event_result.innerHTML = "e.type: " + e.type +
"e.target.id: " + e.target.id +
"e.currentTarget.id: " + e.currentTarget.id +
"e.bubble: " + e.bubble +
"e.cancelable: " + e.cancelable +
"e.defaultPrevented: " + e.defaultPrevented +
"e.eventPhase: " + e.eventPhase + " " +
(
!e.eventPhase ? "NONE"
: ((e.eventPhase==1)?"CAPTURING_PHASE"
:(e.eventPhase==2)?"AT_TARGET"
:"BUBBLING_PHASE")
) +
"e.isTrusted: " + e.isTrusted +
"e.composedPath(): " + e.composedPath() +
"e.timeStamp: " + e.timeStamp;
// var x = document.createEvent("MouseEvent");
// x.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
// for_event.dispatchEvent(x);
}
var event_test_result = document.getElementById("event_test_result");
var img_ok = document.getElementById("img_ok");
var img_nope = document.getElementById("img_nope");
var select_test = document.getElementById("select_test");
var audio_test = document.getElementById("audio_test");
var form_test = document.getElementById("form_test");
var event_input_test = document.getElementById("event_input_test");
img_nope.addEventListener("error", eventsTests);
img_nope.addEventListener("load", eventsTests); // UiEvent
img_nope.addEventListener("abort", eventsTests); // UiEvent
select_test.addEventListener("change", eventsTests);
audio_test.addEventListener("load", eventsTests); // UiEvent
audio_test.addEventListener("abort", eventsTests); // UiEvent
audio_test.addEventListener("loadstart", eventsTests);
audio_test.addEventListener("canplaythrough", eventsTests);
audio_test.addEventListener("canplay", eventsTests);
audio_test.addEventListener("ended", eventsTests);
audio_test.addEventListener("durationchange", eventsTests);
audio_test.addEventListener("seeking", eventsTests);
audio_test.addEventListener("seekend", eventsTests);
audio_test.addEventListener("progress", eventsTests);
audio_test.addEventListener("playing", eventsTests);
audio_test.addEventListener("play", eventsTests);
audio_test.addEventListener("pause", eventsTests);
audio_test.addEventListener("loadedmetadata", eventsTests);
audio_test.addEventListener("loadeddata", eventsTests);
audio_test.addEventListener("volumechange", eventsTests);
audio_test.addEventListener("timeupdate", eventsTests);
audio_test.addEventListener("suspend", eventsTests);
audio_test.addEventListener("stalled", eventsTests);
form_test.addEventListener("reset", eventsTests);
form_test.addEventListener("submit", eventsTests);
event_input_test.addEventListener("invalid", eventsTests);
function eventsTests(e) {
event_test_result.innerHTML += e.target.id + " - " + e.type;
e.preventDefault();
}
// FULLSCREEN
document.addEventListener("fullscreenchange",eventsTests);
document.addEventListener("mozfullscreenchange",eventsTests);
document.addEventListener("webkitfullscreenchange",eventsTests);
document.addEventListener("msfullscreenchange",eventsTests);
document.addEventListener("fullscreenerror",eventsTests);
document.addEventListener("mozfullscreenerror",eventsTests);
document.addEventListener("webkitfullscreenerror",eventsTests);
document.addEventListener("msfullscreenerror",eventsTests);
// element you want displayed in fullscreen
var doc = document.getElementById("event_tests_area");
// open fullscreen mode
function openFullscreen() {
if (doc.requestFullscreen) {
doc.requestFullscreen();
} else if (doc.mozRequestFullScreen) { /* Firefox */
doc.mozRequestFullScreen();
} else if (doc.webkitRequestFullscreen) { /* Chrome, Safari & Opera */
doc.webkitRequestFullscreen();
} else if (doc.msRequestFullscreen) { /* IE/Edge */
doc.msRequestFullscreen();
}
}
// close fullscreen mode
function closeFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
document.body.addEventListener("offline", eventsTests, false);
document.body.addEventListener("online", eventsTests, false);
<iframe src="../js.html" name="iframe_a" id="menu_target" allowfullscreen="true"></iframe>
function simulateClick() {
var event = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
var cb = document.getElementById('checkbox');
var cancelled = !cb.dispatchEvent(event);
if (cancelled) {
// A handler called preventDefault.
alert("cancelled");
} else {
// None of the handlers called preventDefault.
alert("not cancelled");
}
}
var ui_test = document.getElementById("ui_test");
var ui_test_result = document.getElementById("ui_test_result");
// <body onbeforeunload="return myFunction()">
// OR
// window.addEventListener("beforeunload", function(event) {
// event.returnValue = "Write something clever here..";
// });
ui_test.addEventListener("dblclick", uiFunction);
document.getElementsByTagName("BODY")[0].addEventListener("load", uiFunction);
document.getElementsByTagName("BODY")[0].addEventListener("select", uiFunction);
//ui_test_result.addEventListener("scroll", uiFunction);
window.addEventListener("resize", uiFunction);
document.getElementsByTagName("BODY")[0].addEventListener("beforeunload", uiFunction);
document.getElementsByTagName("BODY")[0].addEventListener("unload", uiFunction);
function uiFunction(e) {
ui_test_result.innerHTML = "e.type: " + e.type +
"e.detail: " + e.detail +
( !e.view ? ""
: (
"e.view: " + e.view +
"e.view.outerWidth: " + e.view.outerWidth +
"e.view.outerHeight: " + e.view.outerHeight
) + ("e.view === window: " + (e.view===window))
) +
( !window ? ""
: (
"window: " + window +
"window.outerWidth: " + window.outerWidth +
"window.outerHeight: " + window.outerHeight
)
) +
"----------------------------------------";
}
// <body onload="checkCookies()">
//
// function checkCookies() {
// var text = "";
// if (navigator.cookieEnabled == true) {
// text = "Cookies are enabled.";
// } else {
// text = "Cookies are not enabled.";
// }
// document.getElementById("demo").innerHTML = text;
// }
var kb_test = document.getElementById("kb_test");
var kb_test_result = document.getElementById("kb_test_result");
kb_test.addEventListener("keydown", kbFunction);
kb_test.addEventListener("keypress", kbFunction);
kb_test.addEventListener("keyup", kbFunction);
function kbFunction(e) {
kb_test_result.innerHTML += "e.type: " + e.type +
"e.altKey: " + e.altKey +
"e.ctrlKey: " + e.ctrlKey +
"e.shiftKey: " + e.shiftKey +
"e.metaKey: " + e.metaKey +
"e.charCode: " + e.charCode +
"e.code: " + e.code +
"e.getModifierState('Shift'): " + e.getModifierState('Shift') +
"e.key (keyboard key): " + e.key +
"e.keyCode: " + e.keyCode +
"e.location: " + e.location +
"e.repeat: " + e.repeat +
"e.which: " + e.which +
"String.fromCharCode: " + String.fromCharCode( (e.which||e.keyCode) ) +
"-----------------------------------------------";
var x = e.which || e.keyCode;
if (x == 27) { // 27 is the ESC key
alert ("You pressed the Escape key!");
}
}
var mouse_test = document.getElementById("mouse_test");
mouse_test.addEventListener("click", mouseFunction);
mouse_test.addEventListener("contextmenu", mouseFunction);
mouse_test.addEventListener("dblclick", mouseFunction);
mouse_test.addEventListener("mousedown", mouseFunction);
mouse_test.addEventListener("mouseenter", mouseFunction);
mouse_test.addEventListener("mouseleave", mouseFunction);
mouse_test.addEventListener("mousemove", mouseFunction);
mouse_test.addEventListener("mouseout", mouseFunction);
mouse_test.addEventListener("mouseover", mouseFunction);
mouse_test.addEventListener("mouseup", mouseFunction);
function mouseFunction(e) {
mouse_test.innerHTML = "e.type: " + e.type +
"e.altKey: " + e.altKey +
"e.ctrlKey: " + e.ctrlKey +
"e.shiftKey: " + e.shiftKey +
"e.metaKey: " + e.metaKey +
"e.getModifierState('Shift'): " + e.getModifierState('Shift') +
"e.button: " + e.button +
"e.buttons: " + e.buttons +
"-----------------------------------------------" +
"e.screenX: " + e.screenX +
"e.screenY: " + e.screenY +
"-----------------------------------------------" +
"e.clientX: " + e.clientX +
"e.clientY: " + e.clientY +
"-----------------------------------------------" +
"e.pageX: " + e.pageX +
"e.pageY: " + e.pageY +
"-----------------------------------------------" +
"e.offsetX: " + e.offsetX +
"e.offsetY: " + e.offsetY +
"-----------------------------------------------" +
"e.movementX: " + e.movementX +
"e.movementY: " + e.movementY +
"-----------------------------------------------" +
"e.region: " + e.region +
"e.relatedTarget: " + e.relatedTarget +
"e.which: " + e.which;
var x = e.which || e.keyCode;
if (x == 27) { // 27 is the ESC key
alert ("You pressed the Escape key!");
}
}
var wheel_test = document.getElementById("wheel_test");
wheel_test.addEventListener("wheel", wheelFunction);
function wheelFunction(e) {
wheel_test.innerHTML = "e.type: " + e.type +
"e.deltaX: " + e.deltaX +
"e.deltaY: " + e.deltaY +
"e.deltaZ: " + e.deltaZ +
"e.deltaMode: " + e.deltaMode + " = "+
(
!e.deltaMode ? "pixels" : ( (e.deltaMode==1) ? "lines" : "pages")
);
}
var touch_test = document.getElementById("touch_test");
var touch_test_result = document.getElementById("touch_test_result");
touch_test.addEventListener("touchstart", touchFunction);
touch_test.addEventListener("touchmove", touchFunction);
touch_test.addEventListener("touchend", touchFunction);
touch_test.addEventListener("touchcancel", touchFunction);
function touchFunction(e) {
touch_test_result.innerHTML += "e.type: " + e.type +
"e.targetTouches: " + e.targetTouches +
"e.targetTouches.length: " + e.targetTouches.length +
"e.changedTouches: " + e.changedTouches +
"e.changedTouches.length: " + e.changedTouches.length +
"e.touches: " + e.touches +
"e.touches.length: " + e.touches.length +
"e.touches[0].target.tagName: " + e.touches[0].target.tagName +
"-------------------------------------";
}
// since calling preventDefault() on a touchstart
// or the first touchmove event of a series
// prevents the corresponding mouse events from firing
// it's common to call preventDefault() on touchmove rather than touchstart
// that way, mouse events can still fire and things like links will continue to work
// alternatively, some frameworks have taken to refiring touch events as mouse events
// this example is oversimplified and may result in strange behavior
function onTouch(evt) {
evt.preventDefault();
if (evt.touches.length > 1 || (evt.type == "touchend" && evt.touches.length > 0))
return;
var newEvt = document.createEvent("MouseEvents");
var type = null;
var touch = null;
switch (evt.type) {
case "touchstart":
type = "mousedown";
touch = evt.changedTouches[0];
break;
case "touchmove":
type = "mousemove";
touch = evt.changedTouches[0];
break;
case "touchend":
type = "mouseup";
touch = evt.changedTouches[0];
break;
}
newEvt.initMouseEvent(type, true, true, evt.originalTarget.ownerDocument.defaultView, 0,
touch.screenX, touch.screenY, touch.clientX, touch.clientY,
evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey, 0, null);
evt.originalTarget.dispatchEvent(newEvt);
}
function startup() {
cnv = document.getElementById("touch_canvas");
ctx = cnv.getContext("2d");
cnv.addEventListener("touchstart", handleStart, false);
cnv.addEventListener("touchend", handleEnd, false);
cnv.addEventListener("touchcancel", handleCancel, false);
cnv.addEventListener("touchmove", handleMove, false);
log("initialized.");
}
function log(msg) {
var p = document.getElementById('touch_canvas_log');
p.innerHTML = msg + " - " + p.innerHTML;
}
var ongoingTouches = [],
cnv = null,
ctx = null;
function handleStart(evt) {
// keep the browser from continuing to process the touch event
// (this also prevents a mouse event from also being delivered)
evt.preventDefault();
log("touchstart.");
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
log("touchstart:" + i + "...");
ongoingTouches.push(copyTouch(touches[i]));
var color = colorForTouch(touches[i]);
// 4-pixel wide line
ctx.beginPath();
ctx.arc(touches[i].clientX, touches[i].clientY, 4, 0, 2 * Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
log("touchstart:" + i + ".");
}
}
function handleMove(evt) {
evt.preventDefault();
var touches = evt.changedTouches;
// console.log(touches)
for (var i = 0; i < touches.length; i++) {
var color = colorForTouch(touches[i]);
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
log("continuing touch "+idx);
ctx.beginPath();
log("ctx.moveTo(" + ongoingTouches[idx].clientX + ", " + ongoingTouches[idx].clientY + ");");
ctx.moveTo(ongoingTouches[idx].clientX, ongoingTouches[idx].clientY);
log("ctx.lineTo(" + touches[i].clientX + ", " + touches[i].clientY + ");");
ctx.lineTo(touches[i].clientX, touches[i].clientY);
ctx.lineWidth = 4;
ctx.strokeStyle = color;
ctx.stroke();
ongoingTouches.splice(idx, 1, copyTouch(touches[i])); // swap in the new touch record
log(".");
} else {
log("can't figure out which touch to continue");
}
}
}
function handleEnd(evt) {
evt.preventDefault();
log("touchend");
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
var color = colorForTouch(touches[i]);
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
ctx.lineWidth = 4;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(ongoingTouches[idx].clientX, ongoingTouches[idx].clientY);
ctx.lineTo(touches[i].clientX, touches[i].clientY);
ctx.fillRect(touches[i].clientX - 4, touches[i].clientY - 4, 8, 8); // and a square at the end
ongoingTouches.splice(idx, 1); // remove touch, we're done
} else {
log("can't figure out which touch to end");
}
}
}
function handleCancel(evt) {
evt.preventDefault();
log("touchcancel.");
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
var idx = ongoingTouchIndexById(touches[i].identifier);
ongoingTouches.splice(idx, 1); // remove it; we're done
}
}
function colorForTouch(touch) {
var r = touch.identifier % 16;
var g = Math.floor(touch.identifier / 3) % 16;
var b = Math.floor(touch.identifier / 7) % 16;
r = r.toString(16); // make it a hex digit
g = g.toString(16); // make it a hex digit
b = b.toString(16); // make it a hex digit
var color = "#" + r + g + b;
log("color for touch with identifier " + touch.identifier + " = " + color);
return color;
}
// some browsers (mobile Safari, for one) re-use touch objects between events
// so it's best to copy the bits you care about, rather than referencing the entire object
function copyTouch(touch) {
return { identifier: touch.identifier, clientX: touch.clientX, clientY: touch.clientY };
}
// scans through the ongoingTouches array to find the touch matching the given identifier
// then returns that touch's index into the array
function ongoingTouchIndexById(idToFind) {
for (var i = 0; i < ongoingTouches.length; i++) {
var id = ongoingTouches[i].identifier;
if (id == idToFind) { return i; }
}
return -1; // not found
}
Device Button State | button | buttons |
---|---|---|
Mouse move with no buttons pressed | -1 | 0 |
Left Mouse, Touch Contact, Pen contact (with no modifier buttons pressed) | 0 | 1 |
Middle Mouse | 1 | 4 |
Right Mouse, Pen contact with barrel button pressed | 2 | 2 |
X1 (back) Mouse | 3 | 8 |
X2 (forward) Mouse | 4 | 16 |
Pen contact with eraser button pressed | 5 | 32 |
var pointer_test = document.getElementById("pointer_test");
var pointer_test_inner = document.getElementById("pointer_test_inner");
var pointer_test_result = document.getElementById("pointer_test_result");
pointer_test.addEventListener("pointerenter", pointerFunction);
pointer_test.addEventListener("pointerover", pointerFunction);
pointer_test.addEventListener("pointermove", pointerFunction);
pointer_test.addEventListener("pointerdown", pointerFunction);
pointer_test.addEventListener("pointerup", pointerFunction);
pointer_test.addEventListener("pointercancel", pointerFunction);
pointer_test.addEventListener("pointerout", pointerFunction);
pointer_test.addEventListener("pointerleave", pointerFunction);
pointer_test.addEventListener("gotpointercapture", pointerFunction);
pointer_test.addEventListener("lostpointercapture", pointerFunction);
var prev_action = "";
function pointerFunction(e) {
if (prev_action != e.type) {
pointer_test_inner.innerHTML = e.type + pointer_test_inner.innerHTML;
if(e.type == "pointerleave" || e.type == "pointerout") {
pointer_test_inner.innerHTML = "------------" + pointer_test_inner.innerHTML;
}
}
pointer_test_result.innerHTML = "e.type: " + e.type +
"e.target.id: " + e.target.id +
"e.pointerType: " + (e.pointerType?e.pointerType:"unsuported") +
"e.pointerId: " + e.pointerId +
"e.isPrimary: " + e.isPrimary +
"e.width: " + e.width +
"e.height: " + e.height +
"e.width*e.height: " + (e.width*e.height) +
"e.pressure: " + e.pressure +
"e.tiltX: " + e.tiltX +
"e.tiltY: " + e.tiltY +
"e.screenX: " + e.screenX +
"e.screenY: " + e.screenY +
"e.clientX: " + e.clientX +
"e.clientY: " + e.clientY +
"e.pageX: " + e.pageX +
"e.pageY: " + e.pageY +
"e.offsetX: " + e.offsetX +
"e.offsetY: " + e.offsetY +
"e.movementX: " + e.movementX +
"e.movementY: " + e.movementY +
"e.altKey: " + e.altKey +
"e.ctrlKey: " + e.ctrlKey +
"e.shiftKey: " + e.shiftKey +
"e.metaKey: " + e.metaKey +
"e.getModifierState('Shift'): " + e.getModifierState('Shift') +
"e.button: " + e.button +
"e.buttons: " + e.buttons +
"e.region: " + e.region +
"e.relatedTarget: " + e.relatedTarget +
"e.which: " + e.which;
var x = e.which || e.keyCode;
if (x == 27) { // 27 is the ESC key
alert ("You pressed the Escape key!");
}
prev_action = e.type;
}
function downHandler(ev) {
var el=document.getElementById("target");
// Element "target" will receive/capture further events
el.setPointerCapture(ev.pointerId);
}
function cancelHandler(ev) {
var el=document.getElementById("target");
// Release the pointer capture
el.releasePointerCapture(ev.pointerId);
}
function init() {
var el=document.getElementById("target");
// Register pointerdown and pointercancel handlers
el.onpointerdown = downHandler;
el.onpointercancel = cancelHandler;
}
var input_test = document.getElementById("input_test");
var input_test_result = document.getElementById("input_test_result");
input_test.addEventListener("input", inputFunction);
function inputFunction(event) {
input_test_result.innerHTML += "event.data: " + event.data +
"event.dataTransfer: " + event.dataTransfer +
// "event.getTargetRanges: " + event.getTargetRanges().toString() +
"event.inputType: " + event.inputType +
"-----------------------------------------------";
}
var focus_test = document.getElementById("focus_test_2");
var focus_test_result = document.getElementById("focus_test_result");
focus_test.addEventListener("focusin", focusFunction);
focus_test.addEventListener("focus", focusFunction, true);
focus_test.addEventListener("focusout", focusFunction);
focus_test.addEventListener("blur", focusFunction, true);
function focusFunction(event) {
focus_test_result.innerHTML += event.type+
"relatedTarget: " + event.relatedTarget.id +
"target: " + event.target.id;
}
<input
id="clipboard_test"
type="text"
value="Copy, Cut, Paste values"
/>
<span id="clipboard_test_result"></span>
var clipboard_test = document.getElementById("clipboard_test");
clipboard_test.addEventListener("copy", clipboardFunction);
clipboard_test.addEventListener("cut", clipboardFunction);
clipboard_test.addEventListener("paste", clipboardFunction);
var clipboard_test_result = document.getElementById("clipboard_test_result");
function clipboardFunction(event) {
clipboard_test_result.innerHTML = event.type;
}
<div
class="droptarget"
ondragenter="dragEnter(event)"
ondragover="dragOver(event)"
ondragleave="dragLeave(event)"
ondrop="drop(event)"
style="width:200px;height:300px"
>
<div
id="dragtarget_1"
class="dragtarget"
draggable="true"
ondragstart="dragStart(event)"
ondrag="dragging(event)"
ondragend="dragEnd(event)"
>Dragable_1</div>
// dragable elements ?
</div>
<div
class="droptarget"
ondragenter="dragEnter(event)"
ondragover="dragOver(event)"
ondragleave="dragLeave(event)"
ondrop="drop(event)"
style="width:200px;height:300px"
>
// dragable elements ?
</div>
var drag_info = document.getElementById("drag_info");
var drag_info_2 = document.getElementById("drag_info_2");
// dragable element
function dragStart(event) {
// set data type and the value of the dragged data
event.dataTransfer.setData("Text", event.target.id);
drag_info_2.innerHTML = event.target.id+" drag started";
event.target.style.opacity = "0.4";
}
function dragging(event) {
//drag_info.innerHTML += "element is dragged";
drag_info_2.innerHTML = event.target.id+" element is draged";
drag_info_2.style.color = "red";
}
function dragEnd(event) {
drag_info.innerHTML += event.target.id+" drag ended";
drag_info_2.innerHTML = event.target.id+" drag ended";
event.target.style.opacity = "1";
}
// containers
function dragEnter(event) {
drag_info.innerHTML += event.target.id+" container entrance";
drag_info_2.innerHTML = event.target.id+" container entrance";
if ( event.target.className == "droptarget" ) {
event.target.style.border = "3px dotted red";
}
}
function dragOver(event) {
// allow drop inside element (disabled by default for data/elements)
event.preventDefault();
//drag_info.innerHTML += "element dragging is over";
drag_info_2.innerHTML = event.target.id+" is under draggable";
}
function dragLeave(event) {
event.preventDefault();
drag_info.innerHTML += event.target.id+" out of container";
drag_info_2.innerHTML = event.target.id+" out of container";
if ( event.target.className == "droptarget" ) {
event.target.style.border = "";
}
}
function drop(event) {
// prevent default handling (open as link on drop) of the data
event.preventDefault();
drag_info.innerHTML += event.target.id+" received dragable";
drag_info_2.innerHTML = event.target.id+" received dragable";
if ( event.target.className == "droptarget" ) {
drag_info_2.style.color = "";
event.target.style.border = "";
// get dragged data with the dataTransfer.getData() method
var data = event.dataTransfer.getData("Text");
// append the dragged element into the drop element
event.target.appendChild(document.getElementById(data));
}
}
#transition_test {
width: 100px;
height: 100px;
background: red;
padding:1em;
-webkit-transition: all 1s; /* For Safari 3.1 to 6.0 */
transition: all 1s;
}
#transition_test:hover {
width: 400px;
opacity: 0.3;
}
var transition_test = document.getElementById("transition_test");
transition_test.addEventListener("transitionend", transitionFunction);
transition_test.addEventListener("webkitTransitionEnd", transitionFunction);
//transition_test.addEventListener("mozTransitionEnd", touchFunction);
//transition_test.addEventListener("oTransitionEnd", touchFunction);
transition_test.addEventListener("transitioncancel", transitionFunction);
function transitionFunction(e) {
transition_test.innerHTML = "e.type: " + e.type +
"e.propertyName: " + e.propertyName +
"e.elapsedTime: " + e.elapsedTime +
"e.pseudoElement: " + e.pseudoElement;
}
#animation_test_div {
width: 250px;
height: 100px;
background: orange;
position: relative;
font-size: 20px;
padding:1em;
}
/* Chrome, Safari, Opera */
@-webkit-keyframes my_test_div_move {
from {left: 0px;}
to {left: 200px;}
}
@keyframes my_test_div_move {
from {left: 0px;}
to {left: 200px;}
}
var animation_test_div = document.getElementById("animation_test_div");
// Start the animation with JavaScript
function test_div_move() {
// Chrome, Safari and Opera
animation_test_div.style.WebkitAnimation = "my_test_div_move 4s 5";
// standard syntax
animation_test_div.style.animation = "my_test_div_move 4s 5";
}
// Code for Chrome, Safari and Opera
animation_test_div.addEventListener("webkitAnimationStart", myStartFunction);
animation_test_div.addEventListener("webkitAnimationIteration", myRepeatFunction);
animation_test_div.addEventListener("webkitAnimationEnd", myEndFunction);
// Standard syntax
animation_test_div.addEventListener("animationstart", myStartFunction);
animation_test_div.addEventListener("animationiteration", myRepeatFunction);
animation_test_div.addEventListener("animationend", myEndFunction);
function myStartFunction() {
this.innerHTML = "animationstart";
this.style.backgroundColor = "pink";
}
var iteration = 1;
function myRepeatFunction(event) {
iteration++;
this.innerHTML = "animationiteration: " + iteration +
", elapsed: " + event.elapsedTime + " seconds";
this.style.backgroundColor = "lightblue";
}
function myEndFunction() {
this.innerHTML = "animationend";
this.style.backgroundColor = "lightgray";
}
<body onhashchange="hashFunction(event)">
// window.onhashchange = hashFunction;
function hashFunction(event) {
document.getElementById("hash_test_result").innerHTML += "location.hash: " + location.hash +
"event.newURL: " + event.newURL +
"event.oldURL: " + event.oldURL +
"-----------------------------------------------";
}
window.addEventListener("pageshow", transitionFunction);
window.addEventListener("pagehide", transitionFunction);
function transitionFunction(e) {
console.log(["e.type: "+e.type,"e.persisted: "+e.persisted]);
}
window.onpopstate = function(event) {
console.log([
"location: " + document.location,
"state: " + JSON.stringify(event.state)
]);
};
history.pushState({page: 1}, "keyboard", "#keyboard");
history.pushState({page: 2}, "mouse", "#mouse");
history.replaceState({page: 3}, "pop", "#pop");
history.back();
history.back();
history.go(2);
var storage_test_result = document.getElementById("storage_test_result");
window.addEventListener("storage", storageFunction);
function storageFunction(e) {
storage_test_result.innerHTML = "e.key: " + e.key +
"e.newValue: " + e.newValue +
"e.oldValue: " + e.oldValue +
"e.storageArea: " + e.storageArea +
"e.url: " + e.url;
}
function storageValue() {
var w = window.open("", "myWindow", "width=200,height=100");
if (w.localStorage.clickcount) {
w.localStorage.clickcount = Number(w.localStorage.clickcount)+1;
} else {
w.localStorage.clickcount = 1;
}
w.close();
}
var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { /* ... */ }, false);
// Dispatch the event.
elem.dispatchEvent(event);
// Adding custom data – CustomEvent()
var event = new CustomEvent(
'build',
{ detail: elem.dataset.time }
);
function eventHandler(e) {
console.log('The time is: ' + e.detail);
}
// The old-fashioned way
// Create the event.
var event = document.createEvent('Event');
// Define that the event name is 'build'.
event.initEvent('build', true, true);
// Listen for the event.
elem.addEventListener('build', function (e) {
// e.target matches elem
}, false);
// target can be any Element or other EventTarget.
elem.dispatchEvent(event);
// Event bubbling
<form>
<textarea></textarea>
<//form>
// -----
const form = document.querySelector('form');
const textarea = document.querySelector('textarea');
// Create a new event, allow bubbling
// and provide any data you want to pass to the "details" property
const eventAwesome = new CustomEvent('awesome', {
bubbles: true,
detail: { text: () => textarea.value }
});
// The form element listens for the custom "awesome" event
// and then consoles the output of the passed text() method
form.addEventListener('awesome', e => console.log(e.detail.text()));
// As the user types, textarea inside the form dispatches/triggers the event to fire
// and uses itself as the starting point
textarea.addEventListener('input', e => e.target.dispatchEvent(eventAwesome));
var selection_tr = document.getElementById("selection_tests_result");
var sel = window.getSelection();
function selectionFunction () {
selection_tr.innerHTML =
"sel.toString(): " + sel.toString() +
"sel.anchorNode.tagName: " + sel.anchorNode +
"sel.anchorOffset: " + sel.anchorOffset +
"sel.focusNode.tagName: " + sel.focusNode +
"sel.focusOffset: " + sel.focusOffset +
"sel.isCollapsed: " + sel.isCollapsed +
"sel.type: " + sel.type +
"sel.rangeCount: " + sel.rangeCount;
for (var i=0; i<sel.rangeCount; i++) {
var rng = sel.getRangeAt(i);
selection_tr.innerHTML += "---------------------" +
"rng["+i+"].toString(): " + rng.toString() +
"rng["+i+"].collapsed: " + rng.collapsed +
"rng["+i+"].startContainer: " + rng.startContainer +
" - rng["+i+"].endContainer: " + rng.endContainer +
"rng["+i+"].startOffset: " + rng.startOffset +
" - rng["+i+"].endOffset: " + rng.endOffset +
"rng["+i+"].cloneContents(): " + rng.cloneContents() +
"rng["+i+"].cloneRange(): " + rng.cloneRange() +
"rng["+i+"].getBoundingClientRect(): " + rng.getBoundingClientRect() +
"rng["+i+"].getClientRects(): " + rng.getClientRects() +
"rng["+i+"].commonAncestorContainer: " + rng.commonAncestorContainer;
}
}
setInterval(selectionFunction, 500);
var helpers = {
// debouncing, executes the function if there was no new event in $wait milliseconds
debounce: function (func, wait, scope) {
var timeout;
return function () {
var context = scope || this, args = arguments;
var later = function () {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// in case of a "storm of events", this executes once every $threshold
throttle: function (fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
}
function NIM_demo(){
this.canvas = document.getElementById("paintonme");
this.context = this.canvas.getContext("2d");
this.movearea = document.getElementById("moveonme");
this.canvasTimeScale = 5 * 1000;
this.paintColors = ["#bbd","#464","#d88"];
this.totalLanes = this.paintColors.length;
this.leftMargin = 100;
var self = this;
this.init = function(){
this.canvas.width = window.innerWidth - 250;
this.flush();
this.movearea.addEventListener(
"mousemove",this.regularHandler);
this.movearea.addEventListener(
"mousemove",helpers.debounce(self.debounceHandler,100,this));
this.movearea.addEventListener(
"mousemove",helpers.throttle(self.throttleHander,100,this));
this.movearea.addEventListener(
"touchmove",this.regularHandler);
this.movearea.addEventListener(
"touchmove",helpers.debounce(self.debounceHandler,100,this));
this.movearea.addEventListener(
"touchmove",helpers.throttle(self.throttleHander,100,this));
}
// painting the rectangle / line
this.paintRect = function(lane,time){
if(time > this.canvasTimeScale){
this.startTime += time;
time = 0;
this.flush()
}
// console.log(lane,time);
this.context.fillStyle = this.paintColors[lane];
var x = (this.canvas.width - this.leftMargin) / this.canvasTimeScale * time + this.leftMargin;
var y = this.canvas.height / this.totalLanes * lane;
var height = this.canvas.height / this.totalLanes;
var width = 1;
this.context.fillRect(x,y,width,height);
}
this.flush = function(){
this.context.fillStyle = "#ffffff";
this.context.fillRect(0,0,this.canvas.width,this.canvas.height);
this.context.font = "200 18px Roboto,Helvetica,Arial";
this.context.fillStyle = this.paintColors[0];
this.context.fillText("Regular", 0, 30);
this.context.fillStyle = this.paintColors[1];
this.context.fillText("debounce", 0, 80);
this.context.fillStyle = this.paintColors[2];
this.context.fillText("throttle", 0, 130);
}
// get the time difference
this.getTimeDiff = function(){
var time = new Date().getTime();
if(!this.startTime){ this.startTime = time; }
time -= this.startTime;
return time;
}
this.regularHandler = function(){
self.paintRect(0,self.getTimeDiff());
}
this.debounceHandler = function(){
self.paintRect(1,self.getTimeDiff());
}
this.throttleHander = function(){
self.paintRect(2,self.getTimeDiff());
}
}
var demo = new NIM_demo();
demo.init();
<open-shadow text="I have an open shadow root, "></open-shadow>
<closed-shadow text="I have a closed shadow root"></closed-shadow>
customElements.define('open-shadow',
class extends HTMLElement {
constructor() {
super();
const pElem = document.createElement('p');
pElem.textContent = this.getAttribute('text');
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(pElem);
}});
customElements.define('closed-shadow',
class extends HTMLElement {
constructor() {
super();
const pElem = document.createElement('p');
pElem.textContent = this.getAttribute('text');
const shadowRoot = this.attachShadow({mode: 'closed'});
shadowRoot.appendChild(pElem);
}});
document.querySelector('#webc_result').addEventListener('click', e => {
// console.log(e.composed);
// console.log(e.composedPath());
});
Standard paragraph example text
simple-custom {
background: cyan;
}
:defined {
font-style: italic;
}
simple-custom:not(:defined) {
display: none;
}
simple-custom:defined {
display: block;
}
<simple-custom text="Custom element example text"></simple-custom>
<p>Standard paragraph example text</p>
customElements.define('simple-custom',
class extends HTMLElement {
constructor() {
super();
const divElem = document.createElement('div');
divElem.textContent = this.getAttribute('text');
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(divElem);
}});
My name is
<template id="person-template">
<div>
<h2>Personal ID Card</h2>
<slot name="person-name">NAME MISSING</slot>
<ul>
<li><slot name="person-age">AGE MISSING</slot></li>
<li><slot name="person-occupation">OCCUPATION MISSING</slot></li>
</ul>
</div>
</template>
<person-details>
<p slot="person-name"><edit-word>Morgan</edit-word> Stanley</p>
<span slot="person-age">36</span>
<span slot="person-occupation">Accountant</span>
</person-details>
<p>My name is <edit-word>Chris</edit-word>, the man said.</p>
customElements.define('person-details',
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('person-template');
const templateContent = template.content;
const shadowRoot = this.attachShadow({mode: 'open'});
const style = document.createElement('style');
style.textContent = `
div { padding: 10px; border: 1px solid gray; width: 200px; margin: 10px; }
h2 { margin: 0 0 10px; }
ul { margin: 0; }
p { margin: 10px 0; }
`;
shadowRoot.appendChild(style);
shadowRoot.appendChild(templateContent.cloneNode(true));
}});
customElements.define('edit-word',
class extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
const form = document.createElement('form');
const input = document.createElement('input');
const span = document.createElement('span');
const style = document.createElement('style');
style.textContent = 'span { background-color: #eef; padding: 0 2px }';
shadowRoot.appendChild(style);
shadowRoot.appendChild(form);
shadowRoot.appendChild(span);
span.textContent = this.textContent;
input.value = this.textContent;
form.appendChild(input);
form.style.display = 'none';
span.style.display = 'inline-block';
input.style.width = span.clientWidth + 'px';
this.setAttribute('tabindex', '0');
input.setAttribute('required', 'required');
this.style.display = 'inline-block';
this.addEventListener('click', () => {
span.style.display = 'none';
form.style.display = 'inline-block';
input.focus();
input.setSelectionRange(0, input.value.length)
});
form.addEventListener('submit', e => {
updateDisplay();
e.preventDefault();
});
input.addEventListener('blur', updateDisplay);
function updateDisplay() {
span.style.display = 'inline-block';
form.style.display = 'none';
span.textContent = input.value;
input.style.width = span.clientWidth + 'px';
}}});
<editable-list
title="TODO"
list-item-0="First item on the list"
list-item-1="Second item on the list"
list-item-2="Third item on the list"
list-item-3="Fourth item on the list"
list-item-4="Fifth item on the list"
listItem="This will not appear"
add-item-text="Add new list item:"
>
</editable-list>
(function() {
class EditableList extends HTMLElement {
constructor() {
// establish prototype chain
super();
// attaches shadow tree and returns shadow root reference
// https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow
const shadow = this.attachShadow({ mode: 'open' });
// creating a container for the editable-list component
const editableListContainer = document.createElement('div');
// get attribute values from getters
const title = this.title;
const addItemText = this.addItemText;
const listItems = this.items;
// adding a class to our container for the sake of clarity
editableListContainer.classList.add('editable-list');
// creating the inner HTML of the editable list element
editableListContainer.innerHTML = `
<style>
li, div > div {
display: flex;
align-items: center;
justify-content: space-between;
}
.icon {
background-color: #fff;
border: none;
cursor: pointer;
float: right;
font-size: 1.8rem;
}
</style>
<h3>${title}</h3>
<ul class="item-list">
${listItems.map(item => `
<li>${item}
<button class="editable-list-remove-item icon">⊖</button>
</li>
`).join('')}
</ul>
<div>
<label>${addItemText}</label>
<input class="add-new-list-item-input" type="text"></input>
<button class="editable-list-add-item icon">⊕</button>
</div>
`;
// binding methods
this.addListItem = this.addListItem.bind(this);
this.handleRemoveItemListeners = this.handleRemoveItemListeners.bind(this);
this.removeListItem = this.removeListItem.bind(this);
// appending the container to the shadow DOM
shadow.appendChild(editableListContainer);
}
// add items to the list
addListItem(e) {
const textInput = this.shadowRoot.querySelector('.add-new-list-item-input');
if (textInput.value) {
const li = document.createElement('li');
const button = document.createElement('button');
const childrenLength = this.itemList.children.length;
li.textContent = textInput.value;
button.classList.add('editable-list-remove-item', 'icon');
button.innerHTML = '⊖';
this.itemList.appendChild(li);
this.itemList.children[childrenLength].appendChild(button);
this.handleRemoveItemListeners([...this.itemList.children]);
textInput.value = '';
}
}
// fires after the element has been attached to the DOM
connectedCallback() {
const removeElementButtons = [...this.shadowRoot.querySelectorAll('.editable-list-remove-item')];
const addElementButton = this.shadowRoot.querySelector('.editable-list-add-item');
this.itemList = this.shadowRoot.querySelector('.item-list');
this.handleRemoveItemListeners(removeElementButtons);
addElementButton.addEventListener('click', this.addListItem, false);
}
// gathering data from element attributes
get title() {
return this.getAttribute('title') || '';
}
get items() {
const items = [];
[...this.attributes].forEach(attr =< {
if (attr.name.includes('list-item')) {
items.push(attr.value);
}
});
return items;
}
get addItemText() {
return this.getAttribute('add-item-text') || '';
}
handleRemoveItemListeners(arrayOfElements) {
arrayOfElements.forEach(element =< {
element.addEventListener('click', this.removeListItem, false);
});
}
removeListItem(e) {
e.target.parentNode.remove();
}
}
// let the browser know about the custom element
customElements.define('editable-list', EditableList);
})();
<NEED NAME >
None
dl { margin-left: 6px; }
dt { font-weight: bold; color: #217ac0; font-size: 110% }
dt { font-family: Consolas, "Liberation Mono", Courier }
dd { margin-left: 16px }
<template id="element-details-template">
<style>
details {font-family: "Open Sans Light",Helvetica,Arial}
.name {font-weight: bold; color: #217ac0; font-size: 120%}
h4 { margin: 10px 0 -8px 0; }
h4 span { background: #217ac0; padding: 2px 6px 2px 6px }
h4 span { border: 1px solid #cee9f9; border-radius: 4px }
h4 span { color: white }
.attributes { margin-left: 22px; font-size: 90% }
.attributes p { margin-left: 16px; font-style: italic }
</style>
<details>
<summary>
<span>
<code class="name"><<slot name="element-name">NEED NAME</slot>></code>
<i class="desc"><slot name="description">NEED DESCRIPTION</slot></i>
</span>
</summary>
<div class="attributes">
<h4><span>Attributes</span></h4>
<slot name="attributes"><p>None</p></slot>
</div>
</details>
<hr>
</template>
<element-details>
<span slot="element-name">slot</span>
<span slot="description">A placeholder inside a web
component that users can fill with their own markup,
with the effect of composing different DOM trees
together.</span>
<dl slot="attributes">
<dt>name</dt>
<dd>The name of the slot.</dd>
</dl>
</element-details>
<element-details>
<span slot="element-name">template</span>
<span slot="description">A mechanism for holding client-
side content that is not to be rendered when a page is
loaded but may subsequently be instantiated during
runtime using JavaScript.</span>
</element-details>
customElements.define('element-details',
class extends HTMLElement {
constructor() {
super();
const template = document
.getElementById('element-details-template')
.content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(template.cloneNode(true));
}});
ul {
list-style-type: none;
}
li::before {
display:inline-block;
width: 1rem;
height: 1rem;
margin-right: 0.25rem;
content:"";
}
.open::before, .closed::before {
background-size: 1rem 1rem;
position: relative;
top: 0.25rem;
opacity: 0.3;
}
.open::before {
background-image: url(images/down.png);
}
.closed::before {
background-image: url(images/right.png);
}
.closed .closed::before, .closed .open::before {
display: none;
}
<ul is="expanding-list">
<li>UK
<ul>
<li>Yorkshire
<ul>
<li>Leeds
<ul>
<li>Train station</li>
<li>Town hall</li>
<li>Headrow</li>
</ul>
</li>
<li>Bradford</li>
<li>Hull</li>
</ul>
</li>
</ul>
</li>
<li>USA
<ul>
<li>California
<ul>
<li>Los Angeles</li>
<li>San Francisco</li>
<li>Berkeley</li>
</ul>
</li>
<li>Nevada</li>
<li>Oregon</li>
</ul>
</li>
</ul>
// Create a class for the element
class ExpandingList extends HTMLUListElement {
constructor() {
// Always call super first in constructor
super();
window.onload = function() {
const uls = Array.from(document.querySelectorAll(':root ul'));
const lis = Array.from(document.querySelectorAll(':root li'));
uls.slice(1).forEach(ul => {
ul.style.display = 'none';
});
lis.forEach(li => {
const childText = li.childNodes[0];
const newSpan = document.createElement('span');
newSpan.textContent = childText.textContent;
childText.parentNode.insertBefore(newSpan, childText);
childText.parentNode.removeChild(childText);
});
const spans = Array.from(document.querySelectorAll(':root span'));
spans.forEach(span => {
if (span.nextElementSibling) {
span.style.cursor = 'pointer';
span.parentNode.setAttribute('class', 'closed');
span.onclick = showul;
}
});
function showul(e) {
const nextul = e.target.nextElementSibling;
if (nextul.style.display == 'block') {
nextul.style.display = 'none';
nextul.parentNode.setAttribute('class', 'closed');
} else {
nextul.style.display = 'block';
nextul.parentNode.setAttribute('class', 'open');
}
}
};
}
}
// Define the new element
customElements.define('expanding-list', ExpandingList, { extends: 'ul' });
...
<body>
<header>
<h1>Host selectors <a href="#"><context-span>example</context-span></a></h1>
</header>
<main>
<article>
<h2>This is my first article.</h2>
<p>This article is rather lovely and exciting — it is all about animals,
including <a href="#"><context-span>Beavers</context-span></a>,
<a href="#"><context-span>Bears</context-span></a>,
and <a href="#"><context-span>Wolves</context-span></a>.
I love animals and I'm sure you will too;
please let us know what your favorite animals are. Woo hoo!</p>
</article>
<article>
<h2>This is my second article.</h2>
<p>This article is also quite exciting — it is all about colors,
including <a href="#"><context-span>Red</context-span></a>,
<a href="#"><context-span>Blue</context-span></a>,
and <a href="#"><context-span>Pink</context-span></a>.
A true joy indeed — funky exciting colors make the world go round.
No more gray days for us.</p>
</article>
<aside>
<h2>Some links about web components</h2>
<ul>
<li><a href="#"><context-span>Custom elements</context-span></a></li>
<li><a href="#"><context-span>Shadow DOM</context-span></a></li>
<li><a href="#"><context-span>Templates and slots</context-span></a></li>
</ul>
</aside>
</main>
<footer>
<p>Copyright nobody; example written by
<a href="#"><context-span class="footer">Chris Mills</context-span></a>
</p>
</footer>
</body>
class ContextSpan extends HTMLElement {
constructor() {
super();
const style = document.createElement('style');
const span = document.createElement('span');
span.textContent = this.textContent;
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(style);
shadowRoot.appendChild(span);
style.textContent = `
span:hover { text-decoration: underline; }
:host-context(h1) { font-style: italic; }
:host-context(h1):after { content: " - no links in headers!" }
:host-context(article, aside) { color: gray; }
:host(.footer) { color : red; }
:host { background: rgba(0,0,0,0.1); padding: 2px 5px; }
`;
}
}
// Define the new element
customElements.define('context-span', ContextSpan);
<div>
<button class="add">Add custom-square to DOM</button>
<button class="update">Update attributes</button>
<button class="remove">Remove custom-square from DOM</button>
</div>
// Create a class for the element
class Square extends HTMLElement {
// Specify observed attributes so that
// attributeChangedCallback will work
static get observedAttributes() {
return ['c', 'l'];
}
constructor() {
// Always call super first in constructor
super();
const shadow = this.attachShadow({mode: 'open'});
const div = document.createElement('div');
const style = document.createElement('style');
shadow.appendChild(style);
shadow.appendChild(div);
}
connectedCallback() {
console.log('Custom square element added to page.');
updateStyle(this);
}
disconnectedCallback() {
console.log('Custom square element removed from page.');
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
console.log('Custom square element attributes changed.');
updateStyle(this);
}
}
customElements.define('custom-square', Square);
function updateStyle(elem) {
const shadow = elem.shadowRoot;
const childNodes = Array.from(shadow.childNodes);
childNodes.forEach(childNode => {
if (childNode.nodeName === 'STYLE') {
childNode.textContent = `
div {
width: ${elem.getAttribute('l')}px;
height: ${elem.getAttribute('l')}px;
background-color: ${elem.getAttribute('c')};
}
`;
}
});
}
const add = document.querySelector('.add');
const update = document.querySelector('.update');
const remove = document.querySelector('.remove');
let square;
update.disabled = true;
remove.disabled = true;
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
add.onclick = function() {
// Create a custom square element
square = document.createElement('custom-square');
square.setAttribute('l', '100');
square.setAttribute('c', 'red');
document.getElementById("lcc").appendChild(square);
update.disabled = false;
remove.disabled = false;
add.disabled = true;
};
update.onclick = function() {
// Randomly update square's attributes
square.setAttribute('l', random(50, 200));
square.setAttribute('c', `rgb(${random(0, 255)}, ${random(0, 255)}, ${random(0, 255)})`);
};
remove.onclick = function() {
// Remove the square
document.getElementById("lcc").removeChild(square);
update.disabled = true;
remove.disabled = true;
add.disabled = false;
};
<form>
<div>
<label for="cvc">
Enter your CVC
<popup-info
img="img/alt.png"
data-text="Your card..."
>
</label>
<input type="text" id="cvc">
</div>
</form>
// Create a class for the element
class PopUpInfo extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
// Create a shadow root
const shadow = this.attachShadow({mode: 'open'});
// Create spans
const wrapper = document.createElement('span');
wrapper.setAttribute('class', 'wrapper');
const icon = document.createElement('span');
icon.setAttribute('class', 'icon');
icon.setAttribute('tabindex', 0);
const info = document.createElement('span');
info.setAttribute('class', 'info');
// Take attribute content and put it inside the info span
const text = this.getAttribute('data-text');
info.textContent = text;
// Insert icon
let imgUrl;
if(this.hasAttribute('img')) {
imgUrl = this.getAttribute('img');
} else {
imgUrl = 'img/default.png';
}
const img = document.createElement('img');
img.src = imgUrl;
icon.appendChild(img);
// Create some CSS to apply to the shadow dom
const style = document.createElement('style');
console.log(style.isConnected);
style.textContent = `
.wrapper {
position: relative;
}
.info {
font-size: 0.8rem;
width: 200px;
display: inline-block;
border: 1px solid black;
padding: 10px;
background: white;
border-radius: 10px;
opacity: 0;
transition: 0.6s all;
position: absolute;
bottom: 20px;
left: 10px;
z-index: 3;
}
img {
width: 1.2rem;
}
.icon:hover + .info, .icon:focus + .info {
opacity: 1;
}
`;
// Attach the created elements to the shadow dom
shadow.appendChild(style);
console.log(style.isConnected);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);
}
}
// Define the new element
customElements.define('popup-info', PopUpInfo);
<template id="my-paragraph">
<style>
p {
color: white;
background-color: #666;
padding: 5px;
}
</style>
<p><slot name="my-text">My default text</slot></p>
</template>
<my-paragraph>
<span slot="my-text">Let's have some different text!</span>
</my-paragraph>
<my-paragraph>
<ul slot="my-text">
<li>Let's have some different text!</li>
<li>In a list!</li>
</ul>
</my-paragraph>
customElements.define('my-paragraph',
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-paragraph');
const templateContent = template.content;
this.attachShadow({mode: 'open'}).appendChild(
templateContent.cloneNode(true)
);
}
}
);
const slottedSpan = document.querySelector('my-paragraph span');
console.log(slottedSpan.assignedSlot);
console.log(slottedSpan.slot);
A common, sweet, crun... or yellow in color.
A fairly common, sweet, u...it, usually softer than Apples.
A long, curved, yellow f... fairly gentle flavor.
Orange in color, usually s... sharp, often contains pips.
An orange fruit with big...le, and sweet, juicy flesh.
A red fruit with yell... sweet flavor and a pretty shape.
They are berries and they are blue; swee...und.
<summary-display>
<ul slot="master-list">
<li>Apples</li>
<li>Pears</li>
<li>Bananas</li>
<li>Oranges</li>
<li>Peaches</li>
<li>Strawberries</li>
<li>Blueberries</li>
</ul>
<p data-name="Apples">A common, sweet, crunchy fruit, usually green or yellow in color.</p>
<p data-name="Pears">A fairly common, sweet, usually green fruit, usually softer than Apples.</p>
<p data-name="Bananas">A long, curved, yellow fruit, with a fairly gentle flavor.</p>
<p data-name="Oranges">Orange in color, usually sweet but can be sharp, often contains pips.</p>
<p data-name="Peaches">An orange fruit with big stone in the middle, and sweet, juicy flesh.</p>
<p data-name="Strawberries">A red fruit with yellow seeds on the outside; has a sweet flavor and a pretty shape.</p>
<p data-name="Blueberries">They are berries and they are blue; sweet in flavor, small in size, round.</p>
</summary-display>
<template id="summary-display-template">
<article>
<div>
<slot name="master-list"></slot>
</div>
<div>
<slot name="choice"></slot>
</div>
</article>
</template>
customElements.define('summary-display',
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('summary-display-template');
const templateContent = template.content;
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(templateContent.cloneNode(true));
const items = Array.from(this.querySelectorAll('li'));
const descriptions = Array.from(this.querySelectorAll('p'));
items.forEach(item => {
handleClick(item);
});
function handleClick(item) {
item.addEventListener('click', function() {
items.forEach(item => {
item.style.backgroundColor = 'white';
});
descriptions.forEach(description => {
updateDisplay(description, item);
});
});
}
function updateDisplay(description, item) {
description.removeAttribute('slot');
if(description.getAttribute('data-name') === item.textContent) {
description.setAttribute('slot', 'choice');
item.style.backgroundColor = '#bad0e4';
}
}
const slots = this.shadowRoot.querySelectorAll('slot');
slots[1].addEventListener('slotchange', function(e) {
const nodes = slots[1].assignedNodes();
console.log(`Element in Slot "${slots[1].name}" changed to "${nodes[0].outerHTML}".`);
});
}
}
);
Morgan Stanley
36 AccountantDr. Shazaam
Immortal SuperheroBoris
27 Time traveller
<template id="person-template">
<div>
<h2>Personal ID Card</h2>
<slot name="person-name">NAME MISSING</slot>
<ul>
<li><slot name="person-age">AGE MISSING</slot></li>
<li><slot name="person-occupation">OCCUPATION MISSING</slot></li>
</ul>
</div>
</template>
<person-details>
<p slot="person-name">Morgan Stanley</p>
<span slot="person-age">36</span>
<span slot="person-occupation">Accountant</span>
</person-details>
<person-details>
<p slot="person-name">Dr. Shazaam</p>
<span slot="person-age">Immortal</span>
<span slot="person-occupation">Superhero</span>
</person-details>
<person-details>
<p slot="person-name">Boris</p>
<span slot="age">27</span>
<span slot="i-am-awesome">Time traveller</span>
</person-details>
customElements.define('person-details',
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('person-template');
const templateContent = template.content;
const shadowRoot = this.attachShadow({mode: 'open'});
const style = document.createElement('style');
style.textContent = `
div { padding: 10px; border: 1px solid gray; width: 200px; margin: 10px; }
h2 { margin: 0 0 10px; }
ul { margin: 0; }
p { margin: 10px 0; }
::slotted(*) { color: gray; font-family: sans-serif; }
`;
shadowRoot.appendChild(style);
shadowRoot.appendChild(templateContent.cloneNode(true));
}
});
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar sed justo sed viverra. Aliquam ac scelerisque tellus. Vivamus porttitor nunc vel nibh rutrum hendrerit. Donec viverra vestibulum pretium. Mauris at eros vitae ante pellentesque bibendum. Etiam et blandit purus, nec aliquam libero. Etiam leo felis, pulvinar et diam id, sagittis pulvinar diam. Nunc pellentesque rutrum sapien, sed faucibus urna sodales in. Sed tortor nisl, egestas nec egestas luctus, faucibus vitae purus. Ut elit nunc, pretium eget fermentum id, accumsan et velit. Sed mattis velit diam, a elementum nunc facilisis sit amet.
Pellentesque ornare tellus sit amet massa tincidunt congue. Morbi cursus, tellus vitae pulvinar dictum, dui turpis faucibus ipsum, nec hendrerit augue nisi et enim. Curabitur felis metus, euismod et augue et, luctus dignissim metus. Mauris placerat tellus id efficitur ornare. Cras enim urna, vestibulum vel molestie vitae, mollis vitae eros. Sed lacinia scelerisque diam, a varius urna iaculis ut. Nam lacinia, velit consequat venenatis pellentesque, leo tortor porttitor est, sit amet accumsan ex lectus eget ipsum. Quisque luctus, ex ac fringilla tincidunt, risus mauris sagittis mauris, at iaculis mauris purus eget neque. Donec viverra in ex sed ullamcorper. In ac nisi vel enim accumsan feugiat et sed augue. Donec nisl metus, sollicitudin eu tempus a, scelerisque sed diam.
<article contenteditable="">
<h2>Sample heading</h2>
<p>Lorem ipsum dolor sit amet, ...erisque sed diam.</p>
<p is="word-count"></p>
</article>
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();
// count words in element's parent element
const wcParent = this.parentNode;
function countWords(node){
const text = node.innerText || node.textContent;
return text.split(/\s+/g).length;
}
const count = `Words: ${countWords(wcParent)}`;
// Create a shadow root
const shadow = this.attachShadow({mode: 'open'});
// Create text node and add word count to it
const text = document.createElement('span');
text.textContent = count;
// Append it to the shadow root
shadow.appendChild(text);
// Update count when element content changes
setInterval(function() {
const count = `Words: ${countWords(wcParent)}`;
text.textContent = count;
}, 200);
}
}
// Define the new element
customElements.define('word-count', WordCount, { extends: 'p' });