Skip to content

Commit 03064e6

Browse files
author
Ryan A. Johnson
committed
refactor(HXElement): make HXElement more efficient
- Only generate ShadyCSS once - Only generate ShadyCSS if it's actually needed on the page - Only prepare the ShadyDOM template once - Update regression snapshots
1 parent 1307a08 commit 03064e6

File tree

5 files changed

+3960
-67
lines changed

5 files changed

+3960
-67
lines changed

src/helix-ui/elements/HXElement.js

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { KEYS } from '../util';
22

3+
// Keep track of prepared ShadyDOM templates
4+
const SHADY_TEMPLATES = {};
5+
36
/**
47
* @external HTMLElement
58
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement" target="_blank">MDN - HTMLElement</a>
@@ -99,7 +102,10 @@ export class HXElement extends HTMLElement {
99102
*/
100103
$onAttributeChange (attr, oldVal, newVal) {} // eslint-disable-line no-unused-vars
101104

102-
// Define an element using the customElements registry.
105+
/**
106+
* Register class with the customElements registry.
107+
* Note: the custom element is only registered if the "is" class property is defined.
108+
*/
103109
static $define () {
104110
if (this.is) {
105111
customElements.define(this.is, this);
@@ -109,21 +115,7 @@ export class HXElement extends HTMLElement {
109115
// Called when an instance is created
110116
constructor () {
111117
super();
112-
113-
// Don't attach shadow DOM unless "template" class property is defined.
114-
if (this.constructor.template) {
115-
let _template = document.createElement('template');
116-
_template.innerHTML = this.constructor.template;
117-
118-
this.attachShadow({ mode: 'open' });
119-
120-
if (window.ShadyCSS) {
121-
ShadyCSS.prepareTemplate(_template, this.constructor.is);
122-
ShadyCSS.styleElement(this);
123-
}
124-
125-
this.shadowRoot.appendChild(_template.content.cloneNode(true));
126-
}
118+
this._$setupShadowDOM();
127119

128120
this.$onAttributeChange = this.$onAttributeChange.bind(this);
129121
this.$onConnect = this.$onConnect.bind(this);
@@ -151,29 +143,29 @@ export class HXElement extends HTMLElement {
151143
* @type {Array<String>}
152144
*/
153145
static get observedAttributes () {
154-
return [ 'disabled' ].concat(this.$observedAttributes);
146+
let common = [ 'disabled' ];
147+
let extra = this.$observedAttributes;
148+
return [ ...common, ...extra ];
155149
}
156150

157151
// Called when an attribute UPDATES (not just when it changes).
158152
attributeChangedCallback (attr, oldVal, newVal) {
159-
switch (attr) {
160-
case 'disabled':
161-
if (newVal !== null) {
162-
this.removeAttribute('tabindex');
163-
this.setAttribute('aria-disabled', true);
164-
this.blur();
165-
} else {
166-
this.setAttribute('tabindex', this._$tabIndex);
167-
this.removeAttribute('aria-disabled');
168-
}
169-
break;
153+
if (attr === 'disabled') {
154+
if (newVal !== null) {
155+
this.removeAttribute('tabindex');
156+
this.setAttribute('aria-disabled', true);
157+
this.blur();
158+
} else {
159+
this.setAttribute('tabindex', this._$tabIndex);
160+
this.removeAttribute('aria-disabled');
161+
}
162+
}
170163

171-
default:
172-
if (newVal !== oldVal) {
173-
this.$onAttributeChange(attr, oldVal, newVal);
174-
}
175-
break;
176-
}//switch
164+
// Always call $onAttributeChange, so that we can run additional
165+
// logic against common attributes in subclasses, too.
166+
if (newVal !== oldVal) {
167+
this.$onAttributeChange(attr, oldVal, newVal);
168+
}
177169
}//attributeChangedCallback
178170

179171
/**
@@ -320,4 +312,50 @@ export class HXElement extends HTMLElement {
320312
this.removeAttribute('disabled');
321313
}
322314
}
315+
316+
/**
317+
* @private
318+
* @description
319+
* If the browser doesn't have native ShadowDOM, this method
320+
* will ensure that the ShadyDOM template is prepared no more
321+
* than once, and applies ShadyDOM styling to the element.
322+
*
323+
* @param {HTMLTemplate} template
324+
*/
325+
_$setupShadyDOM (template) {
326+
let _elementName = this.constructor.is;
327+
328+
if (window.ShadyCSS) {
329+
// check to see if the ShadyDOM template has already been prepared
330+
if (!SHADY_TEMPLATES[_elementName]) {
331+
// modifies 'template' variable in-place
332+
ShadyCSS.prepareTemplate(template, _elementName);
333+
// memoize prepared template, so it isn't prepared more than once
334+
SHADY_TEMPLATES[_elementName] = template;
335+
}
336+
// Apply ShadyDOM styling (rewrites Light DOM)
337+
ShadyCSS.styleElement(this);
338+
}
339+
}//_$setupShadyDOM
340+
341+
/**
342+
* @private
343+
* @description
344+
* If a ShadowDOM needs to be setup, this method handles:
345+
*
346+
* 1. creating the <template> element
347+
* 2. attaching a shadow root
348+
* 3. applying ShadyDOM styling (if needed)
349+
* 4. stamping the template
350+
*/
351+
_$setupShadowDOM () {
352+
// Don't do anything unless the "template" class property is defined.
353+
if (this.constructor.template) {
354+
let _template = document.createElement('template');
355+
_template.innerHTML = this.constructor.template;
356+
this.attachShadow({ mode: 'open' });
357+
this._$setupShadyDOM(_template);
358+
this.shadowRoot.appendChild(_template.content.cloneNode(true));
359+
}
360+
}//_$setupShadowDOM()
323361
}
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
@import (reference) "vars";
22

3-
*, *::before, *::after {
4-
box-sizing: border-box;
5-
color: inherit;
6-
font: inherit;
7-
letter-spacing: inherit;
8-
}
9-
10-
input::-ms-clear {
11-
display: none;
3+
:host {
4+
*, *::before, *::after {
5+
box-sizing: border-box;
6+
// TODO: convert below properties into .inheritTypography() mixin
7+
color: inherit;
8+
font: inherit;
9+
letter-spacing: inherit;
10+
}
1211
}

src/helix-ui/elements/HXSearchElement.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
@import "./HXElement.less";
33
@import "elements/hx-icon";
44

5+
input::-ms-clear {
6+
display: none;
7+
}
8+
59
:host {
610
display: block;
711
font-size: 1rem;

0 commit comments

Comments
 (0)