Last active
February 8, 2024 17:44
-
-
Save amcgregor/b92e981fdcf4a85cb30242581732f07a to your computer and use it in GitHub Desktop.
Currently untested ES6 notepad sketches of isolatable global behaviors.
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
{ // "Change delay" committal of altered form fields and editable content without waiting for blur. | |
function monitorTyping( | |
element, | |
initial = 1000, // Initial delay value of one second. | |
highlight = true, // When entering a field, select its contents. | |
minimum = 500, // Minimum wait time. | |
threshold = 2, // Don't bother if the field has fewer than this many characters. | |
forceable = true // Ignore threshold on blur and when pressing enter. | |
) { | |
var timeout, | |
lastChange = 0, | |
delay = changeDelayDefaults.initial, | |
value = element.attributes.contenteditable ? element.innerHTML : element.value | |
if ( // Only handle contenteditable, <input type="text"> and <textarea> tags. | |
!element.attributes.contenteditable && | |
element.type.toUpperCase() != "TEXT" && | |
element.nodeName.toUpperCase() != "TEXTAREA" | |
) return null; | |
function handler(forced=false) { | |
lastChange = 0; | |
var nvalue = element.attributes.contenteditable ? element.innerHTML : element.value; | |
// If nothing has changed, or the content length is below our threshold, nothing to do. | |
if ( !forced && (nvalue == value || nvalue.length < threshold) ) { | |
value = nvalue | |
return; | |
} | |
let changed = new Event('change') | |
element.dispatchEvent(changed) | |
} | |
function check(e) { | |
clearTimeout(timeout) | |
if ( event.keyCode == 13 && this.type.toUpperCase() == "TEXT" ) handler(forceable) | |
if ( lastChange === 0 ) { | |
lastChange = Date.now() | |
delay = initial | |
timeout = setTimeout(handler, delay) | |
return | |
} | |
} | |
element.addEventListener('keyup', check) | |
element.addEventListener('blur', e => handler(forceable)) | |
if ( highlight ) element.addEventListener('focus', e => try { | |
if ( e.target.attributes.contenteditable ) window.getSelection().selectAllChildren(e.target) | |
else e.target.select() | |
} catch (e) {}) | |
return element | |
} | |
} | |
/* Port of https://github.com/amcgregor/change-delay/blob/master/jquery.change-delay.js - requires testing. */ |
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
{ // Collapse opened menus if focused activation target clicked again. | |
for ( let elem of [...document.querySelectorAll('header#top li > label')] ) | |
elem.addEventListener('mousedown', e => { | |
if ( e.target.matches(':focus') ) { | |
e.target.blur() | |
e.preventDefault() | |
} | |
}) | |
} | |
/* Tested as working via https://codepen.io/amcgregor/project/live/9f171b58c35d32fd02f13bef0778b311 */ |
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
// As this "component" is slightly more involved, it has its own Gist: | |
// https://gist.github.com/amcgregor/f3529394f032ae517b0a02a0edbac7f7 |
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
{ // Detect scrolling of the viewport away from the absolute top of the document. | |
function scrollHandler(e) { | |
if ( window.scrollY == 0 && document.body.scrollTop == 0 ) document.body.classList.remove('offset') | |
else document.body.classList.add('offset') | |
} | |
// Watch for the page being srolled. | |
window.addEventListener('scroll', scrollHandler) | |
document.body.addEventListener('scroll', scrollHandler) | |
} | |
/* Tested as working via https://codepen.io/amcgregor/project/live/9f171b58c35d32fd02f13bef0778b311 */ |
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
{ // Page visibility detection (level) and event (edge) response for user attentiveness inference and detection. | |
var stateAttribute = null // Document object attribute name holding the current (level) visbility state. | |
const v = "active", h = "inactive", // Mapping of "visible" and "hidden" levels to CSS class names. | |
stateMap = { // CSS body element mapping of visibility event (edge) to CSS class name transitioning to. | |
focus: v, focusin: v, pageshow: v, | |
blur: h, focusout: h, pagehide: h, | |
} | |
function visibilityChanged(e) { // Generalized handler covering both explicit and inferred visibility. | |
e = e || window.event | |
document.body.classList.remove(h, v) // Remove both, the now-active state will be added back. | |
if ( e.type in stateMap ) document.body.classList.add(stateMap[e.type]) | |
else document.body.classList.add(this[stateAttribute] ? h : v) | |
} | |
// Attach browser-specific or generic handlers as appropriate. | |
for ( var prefix of ['moz', 'webkit', 'ms', ''] ) { | |
let attr = prefix + 'Hidden' | |
attr = attr[0].toLowerCase() + attr.substr(1) | |
if ( attr in document ) { | |
document.addEventListener(prefix + 'visibilitychange', visibilityChanged) | |
stateAttribute = attr | |
break | |
} | |
} | |
// Handle all other browsers through inference from available events. Skipped: user activity detection. | |
if ( stateAttribute === null ) window.onpageshow = window.onpagehide = window.onfocus = window.onblur = visibilityChanged; | |
// Set initial state if detection is possible by invoking the handler with a synthesized event. | |
if ( document[stateAttribute] !== undefined ) visibilityChanged({type: document[stateAttribute] ? 'blur' : 'focus'}) | |
} | |
/* New development, requires testing. */ |
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
// As there are many prototype mutations to issue to add Python-like functionality, it has its own Gist: | |
// https://gist.github.com/amcgregor/0c79746b8391da346c7590ddac6b1881 |
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
// Formerly: https://gist.github.com/amcgregor/5b479dbf167a244454780434a12885be | |
{ // Detect scrolling of the viewport away from the absolute top of the document. | |
function scrollHandler(e) { | |
if ( window.scrollY == 0 && document.body.scrollTop == 0 ) document.body.classList.remove('offset') | |
else document.body.classList.add('offset') | |
} | |
// Watch for the page being scrolled. | |
window.addEventListener('scroll', scrollHandler) | |
document.body.addEventListener('scroll', scrollHandler) | |
} |
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
{ // ARIA accessible tabbed interface navigation. | |
document.body.addEventListener('focus', e => { | |
let parent = findParent(e.target, '[role=tablist]') | |
let container = findParent(event.target, 'dd[role=tabpanel]') | |
if ( !parent || !container ) return // We weren't contained within a tabset or tab. | |
if ( !container.previousSibling.matches("[aria-selected]") ) container.previousSibling.click() | |
}, {capture: true}) // Capture on the way down, not on the route back up. | |
// This should "switch tab" before focusing the element. | |
for ( let elem of [...document.querySelectorAll('[role=tablist] [role=tab]')] ) { | |
elem.addEventListener('click', e => { | |
for ( let sibling of [...e.target.parentElement.querySelectorAll('[aria-selected]')] ) | |
sibling.ariaSelected = undefined | |
e.target.ariaSelected = true | |
}) | |
elem.addEventListener('keydown', e => { | |
var target = null; | |
switch ( event.key ) { | |
case ' ': | |
case 'Enter': | |
target = event.target | |
break | |
case 'ArrowLeft': | |
case 'ArrowUp': | |
case 'h': // Vim | |
case 'k': | |
case 'w': // Gaming | |
case 'a': | |
if ( e.target.ariaSelected ) | |
target = getPreviousElement(event.target, '[role=tab]') | |
break | |
case 'ArrowRight': | |
case 'ArrowDown': | |
case 'l': // Vim | |
case 'j': | |
case 's': // Gaming | |
case 'd': | |
if ( e.target.ariaSelected ) | |
target = getNextElement(event.target, '[role=tab]') | |
break | |
} | |
if ( !target ) return; | |
target.focus() | |
target.click() | |
e.preventDefault() | |
e.stopPropagation() | |
}) | |
} | |
} | |
/* Tested as working via https://codepen.io/amcgregor/pen/yLgdrey */ |
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
// My implementation is AKA "Relativistic". | |
// Present <time> elements as relative to now while preserving the original exact date+time. | |
// Monitor for element injection and attribute manipulation to populate or update the relative time presentations. | |
// As time progresses, keep the relative presentations accurate. | |
// Actual repository: https://github.com/amcgregor/es6-relativistic | |
// Gist: https://gist.github.com/amcgregor/c675c7ef41156be802976d577c0bd159 |
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
{ // In the event the page title is truncated, permit hover tooltip w/o data model duplication. | |
const pageTitle = document.querySelector('header#top h2') | |
pageTitle.title = pageTitle.innerText | |
function titleChanged(m) { | |
// TODO: Determine if the event is fired pre- or post- update of the document.title | |
mutations.forEach(function(mutation) { | |
pageTitle.title = pageTitle.innerText = mutation.target.textContent.split('-')[0].trim() | |
}); | |
pageTitle.title = pageTitle.innerText = document.title.split('-')[0].trim() | |
} | |
const titleObserver = new MutationObserver(titleChanged); | |
titleObserver.observe(document.head.querySelector('title'), {subtree: true, characterData: true, childList: true}) | |
} | |
/* Title attribute assignment tested as working via https://codepen.io/amcgregor/project/live/9f171b58c35d32fd02f13bef0778b311 */ | |
/* Observation and reaction to changes requires testing. */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment