What should the javascript do?
mostly booleans
Image credit: Clker-Free-Vector-Images
How do we ensure access?
How do we keep it usable?
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium sint cupidatat.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium sint cupidatat.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium sint cupidatat.
Code: Progressive Enhancement
Sed ut perspiciatis unde omnis iste natus error sit
voluptatem accusantium sint cupidatat.
Sed ut perspiciatis unde omnis iste natus error sit
voluptatem accusantium sint cupidatat.
Code: Disclosure Markup
class Disclosure {
trigger;
constructor(trigger) {
this.trigger = trigger;
console.log('Instantiated new Disclosure object.');
}
}
(function() {
const triggers = document.querySelectorAll('.latest-news__summary-toggle');
if (!triggers) {
return;
}
triggers.forEach((trigger) => new Disclosure(trigger));
})();
Code: Class File
enable = () => null;
disable = () => null;
expand = () => null;
collapse = () => null;
isEnabled = () => null;
isExpanded = () => null;
Code: Public API
enable = () => null;
enable = () => this._control();
disable = () => null;
disable = () => this._control(false);
expand = () => null;
expand = () => this._toggle();
collapse = () => null;
collapse = () => this._toggle(false);
isEnabled = () => null;
isEnabled = () => this.enabled;
isExpanded = () => null;
isExpanded = () => this.expanded;
Code: Public API Plumbing
_createEvent = (type) => null;
_createEvent = (type) => new CustomEvent(this.name, {
bubbles: true,
detail: {action: type}
});
Code: Custom Events
_init = () => {
try {
this.enable();
} catch (error) {
console.error(error);
}
};
Code: Enable and Disable
_control = (enable = true) => {
const action = enable ? 'enable' : 'disable';
if (enable) {
this.trigger.addEventListener('click', this._handleClick);
this.trigger.hidden = false;
} else {
this.trigger.removeEventListener('click', this._handleClick);
this.trigger.hidden = true;
}
this._toggle(this.defaultExpanded);
this.trigger.dispatchEvent(this._createEvent(action));
this.enabled = enable;
};
Code: Enable and Disable
_handleClick = () => this._toggle(!this.expanded);
_toggle = (expand = true) => {
const action = expand ? 'expand' : 'collapse';
this.trigger.dispatchEvent(this._createEvent(action));
this.trigger.setAttribute('aria-expanded', expand);
this.expanded = expand;
};
Code: Expand and Collapse
this._init(); // called by: constructor()
this.enable(); // called by: _init()
this._control(); // called by: enable(), disable()
this._handleClick(); // enabled by: _control()
this._toggle(); // called by: _handleClick(), expand(), collapse()
this._createEvent(); // called by: _control(), _toggle()
Code: Minimum Viable Product
Code: Minimum Viable Product
Adipisci velit, sed quia non numquam eius modi tempora
incidunt ut labore et dolore magnam aliquam.
/* Styles */
.toggle[aria-expanded="true"]:not([hidden]) + p {
display: block;
}
.toggle[aria-expanded="false"]:not([hidden]) + p {
display: none;
}
too opinionated!
Code: Settings and Contents
(Multiple) Non-Sibling Elements
constructor(trigger, contents = undefined)
this.contents = this._getContents(contents);
_toggle = (expand = true) => {
const action = expand ? 'expand' : 'collapse';
this.trigger.dispatchEvent(this._createEvent(action));
this.trigger.setAttribute('aria-expanded', expand);
this.contents.forEach((content) =>
content.setAttribute('data-disclosure-expanded', expand));
this.expanded = expand;
};
Code: Settings and Contents
Manually run enable()
class Disclosure {
contents;
defaultExpanded;
defaults = {
autoStart: true,
};
enabled;
expanded;
name = 'disclosure';
trigger;
constructor(trigger, contents = undefined, settings = {}) {
this.trigger = trigger;
this.contents = this._getContents(contents);
this.settings = {...this.defaults, ...settings};
this.defaultExpanded = this.trigger.getAttribute('aria-expanded') === 'true';
this.trigger.disclosure = this;
this._init();
}
_control = (enable = true) => {
const action = enable ? 'enable' : 'disable';
if (enable) {
this.trigger.addEventListener('click', this._handleClick);
this.trigger.hidden = false;
} else {
this.trigger.removeEventListener('click', this._handleClick);
this.trigger.hidden = true;
}
this._toggle(this.defaultExpanded);
this.trigger.dispatchEvent(this._createEvent(action));
this.enabled = enable;
};
_createEvent = (type) => new CustomEvent(this.name, {
bubbles: true,
detail: {action: type}
});
_getContents = (contents) => {
let contentsList;
if (typeof contents !== 'undefined') {
contentsList = typeof contents[Symbol.iterator] === 'function'
? [...contents]
: [contents]
} else {
contentsList = [this.trigger.nextElementSibling];
}
return contentsList;
};
_handleClick = () => this._toggle(!this.expanded);
_init = () => {
try {
if (this.settings.autoStart) {
this.enable();
}
} catch (error) {
console.error(error);
}
};
_toggle = (expand = true) => {
const action = expand ? 'expand' : 'collapse';
this.trigger.dispatchEvent(this._createEvent(action));
this.trigger.setAttribute('aria-expanded', expand);
this.contents.forEach((content) =>
content.setAttribute('data-disclosure-expanded', expand));
this.expanded = expand;
};
collapse = () => this._toggle(false);
disable = () => this._control(false)
enable = () => this._control()
expand = () => this._toggle();
isEnabled = () => this.enabled;
isExpanded = () => this.expanded;
}
Code: Settings and Contents