Published
Edited
Jul 6, 2022
Importers
28 stars
Insert cell
Insert cell
Insert cell
Insert cell
createEventList(document, {invalidation, onClick: e => mutable event = e})
Insert cell
Insert cell
Insert cell
Insert cell
{
const defaults = {
invalidation,
bubbling: includeBubbling,
useCapture: true,
duration: 5000,
};
const input = html`<input placeholder="Type something ...">`;
const label = html`<label>Label ${input}</label>`;
const filterMouse = n => n.match(/mouse|pointer|click|contextmenu|wheel/);
const filterKey = n => n.match(/^key/);

const mouseList = createEventList(label, { ...defaults, filter: filterMouse });
const keyList = createEventList(label, { ...defaults, filter: filterKey });
const otherList = createEventList(label, { ...defaults, filter: n => !filterMouse(n) && !filterKey(n) });

// Sets a custom animation timing function, ticking down every quarter second.
//for(let l of [mouseList, keyList, otherList]) l.style.setProperty('--bar-timing-func', `steps(12, start)`);
return html`<div class=ex>
${label}<br>
<dl>
<dt>Mouse events</dt><dd>${mouseList}</dd>
<dt>Key events</dt><dd>${keyList}</dd>
<dt>Other events</dt><dd>${otherList}</dd>
`;
}
Insert cell
Insert cell
function createEventList(target, options = {}) {
const {
invalidation: invalidated = invalidation,
// Capture bubbled events.
bubbling = true,
useCapture = false,
eventList = null,
onClick = logToConsole,
group = e => e.constructor.name,
filter = (type) => true,
hash = true,
sortRecent = false,
layout = 'row',
fontSize = '.7em',
duration = 4000,
className = `event-list-${Date.now().toString(36)}-${(Math.random()*1e10|0).toString(36)}`,
} = options;

const groups = new Map();
const events = eventList || getEventTypes(target, filter);
const hashKey = typeof hash === 'function' ? hash : e => e.constructor.name;
const types = new Map();
const handleClick = function() {
if(!this.lastEvent) return;
onClick.call(this, this.lastEvent, this.lastTimeStamp);
}

const list = html`<div ${{
class: className,
style: {
'--layout': layout,
'--font-size': fontSize,
'--bar-duration': `${duration / 1000}s`
},
}}>${getStyle(className)}`;
for(const type of events) {
target.addEventListener(type, handleEvent, useCapture || undefined);
invalidated.then(() => {
target.removeEventListener(type, handleEvent, useCapture || undefined);
});
}
return list;

function handleEvent(e) {
if(!bubbling && e?.target !== target) return;
const el = types.get(e.type) || addEvent(e, list);
if(hash) {
const key = hashKey(e);
if(el.hashKey !== key) {
el.hashKey = key;
el.style.setProperty('--bar-color', colorHash(key, 0, 360));
}
}
el.lastEvent = e;
el.lastTimeStamp = Date.now();
el.dataset.count++;
// Avoid reflow when using variable-width fonts.
el.style.setProperty('--digits', el.dataset.count.toString().length);
if(sortRecent) list.insertBefore(el, list.firstElementChild);
else list.insertBefore(el, el.nextElementSibling)
}
function addEvent(e, list) {
const type = e.type;
const element = html`<span>${type}`;
types.set(type, element);
element.dataset.type = type;
element.dataset.parentType = e.constructor.name;
element.dataset.count = 0;
element.onclick = handleClick;

return insertSorted(element, list, node => node.dataset.type > type);
}

function insertSorted(element, parent, findBefore) {
return parent.insertBefore(
element,
Array.prototype.find.call(parent.children, findBefore)
);
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more