Stupid Simple

Stupid Simple

A Functionality

What should the javascript do?

mostly booleans

Image credit: Clker-Free-Vector-Images

B Accessibility

How do we ensure access?

progressive enhancement

graceful degradation

WAI-ARIA

Image credit: Google Fonts

C Re-usability

How do we keep it usable?

00 The Disclosure

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium sint cupidatat.

01 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: Progressive Enhancement

02 Disclosure Markup

                  
                    

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

03 Class File

                  
                    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

04 Public API

                
                  enable = () => null;
                
              
                
                  disable = () => null;
                
              
                
                  expand = () => null;
                
              
                
                  collapse = () => null;
                
              
                
                  isEnabled = () => null;
                
              
                
                  isExpanded = () => null;
                
              

Code: Public API

04 Public API (events)

                
                  _createEvent = (type) => null;
                
              

Code: Public API

05 Public API Plumbing

                  
                    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

06 Custom Events

                  
                    _createEvent = (type) => null;
                  
                
                  
                    _createEvent = (type) => new CustomEvent(this.name, {
                      bubbles: true,
                      detail: {action: type}
                    });
                  
                

Code: Custom Events

07 Enable and Disable

                
                  _init = () => {
                    try {
                      this.enable();
                    } catch (error) {
                      console.error(error);
                    }
                  };
                
              

Code: Enable and Disable

07 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

08 Expand and Collapse

                
                  _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

09 Minimum Viable Product

                
                    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

09 Minimum Viable Product

  • Checked checkbox progressive enhancement
  • Checked checkbox WAI-ARIA
  • Checked checkbox enable / disable
  • Checked checkbox expand / collapse
  • Checked checkbox custom events
  • Unchecked checkbox non-sibling elements
  • Unchecked checkbox multiple disclosures
  • Unchecked checkbox manual enable

Code: Minimum Viable Product

10 Settings and Contents

                  
                    
                    
                    

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

10 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

10 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

Stupid Simple