From 1d305b55114bffaece2841500c8476ba3996d2b2 Mon Sep 17 00:00:00 2001 From: David Zearing Date: Tue, 24 Apr 2018 19:34:37 -0700 Subject: [PATCH] Merge styles order (#4664) * Updating how style elements are injected (to preserve the location in head in which they're inserted.) * adding change file * Adding nit fixes. --- .../merge-styles-order_2018-04-24-16-59.json | 11 +++ packages/merge-styles/src/Stylesheet.ts | 72 ++++++++++--------- 2 files changed, 50 insertions(+), 33 deletions(-) create mode 100644 common/changes/@uifabric/merge-styles/merge-styles-order_2018-04-24-16-59.json diff --git a/common/changes/@uifabric/merge-styles/merge-styles-order_2018-04-24-16-59.json b/common/changes/@uifabric/merge-styles/merge-styles-order_2018-04-24-16-59.json new file mode 100644 index 0000000000000..59eca511c07b0 --- /dev/null +++ b/common/changes/@uifabric/merge-styles/merge-styles-order_2018-04-24-16-59.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@uifabric/merge-styles", + "comment": "merge-styles: `style` elements which are created on the fly now \"bunch\" together, avoiding unpredictability in specificity.", + "type": "patch" + } + ], + "packageName": "@uifabric/merge-styles", + "email": "dzearing@microsoft.com" +} \ No newline at end of file diff --git a/packages/merge-styles/src/Stylesheet.ts b/packages/merge-styles/src/Stylesheet.ts index c3b1a87a52ee6..f86b551edd5c9 100644 --- a/packages/merge-styles/src/Stylesheet.ts +++ b/packages/merge-styles/src/Stylesheet.ts @@ -50,6 +50,7 @@ let _stylesheet: Stylesheet; * @public */ export class Stylesheet { + private _lastStyleElement?: HTMLStyleElement; private _styleElement?: HTMLStyleElement; private _rules: string[] = []; private _config: IStyleSheetConfig; @@ -158,28 +159,29 @@ export class Stylesheet { public insertRule( rule: string ): void { - const element = this._getElement(); - const injectionMode = element ? this._config.injectionMode : InjectionMode.none; - - switch (injectionMode) { - case InjectionMode.insertNode: - const { sheet } = element!; - - try { - // tslint:disable-next-line:no-any - (sheet as any).insertRule(rule, (sheet as any).cssRules.length); - } catch (e) { - /* no-op on errors */ - } - break; - - case InjectionMode.appendChild: - _createStyleElement(rule); - break; - - default: - this._rules.push(rule); - break; + const { injectionMode } = this._config; + const element = injectionMode !== InjectionMode.none ? this._getStyleElement() : undefined; + + if (element) { + switch (this._config.injectionMode) { + case InjectionMode.insertNode: + const { sheet } = element!; + + try { + (sheet as CSSStyleSheet).insertRule(rule, (sheet as CSSStyleSheet).cssRules.length); + } catch (e) { + // The browser will throw exceptions on unsupported rules (such as a moz prefix in webkit.) + // We need to swallow the exceptions for this scenario, otherwise we'd need to filter + // which could be slower and bulkier. + } + break; + + case InjectionMode.appendChild: + element.appendChild(document.createTextNode(rule)); + break; + } + } else { + this._rules.push(rule); } if (this._config.onInsertRule) { @@ -212,9 +214,9 @@ export class Stylesheet { this._keyToClassName = {}; } - private _getElement(): HTMLStyleElement | undefined { + private _getStyleElement(): HTMLStyleElement | undefined { if (!this._styleElement && typeof document !== 'undefined') { - this._styleElement = _createStyleElement(); + this._styleElement = this._createStyleElement(); // Reset the style element on the next frame. window.requestAnimationFrame(() => { @@ -223,17 +225,21 @@ export class Stylesheet { } return this._styleElement; } -} -function _createStyleElement(content?: string): HTMLStyleElement { - const styleElement = document.createElement('style'); + private _createStyleElement(): HTMLStyleElement { + const styleElement = document.createElement('style'); + + styleElement.setAttribute('data-merge-styles', 'true'); + styleElement.type = 'text/css'; + + if (this._lastStyleElement && this._lastStyleElement.nextElementSibling) { + document.head.insertBefore(styleElement, this._lastStyleElement.nextElementSibling); + } else { + document.head.appendChild(styleElement); + } + this._lastStyleElement = styleElement; - styleElement.setAttribute('data-merge-styles', 'true'); - styleElement.type = 'text/css'; - if (content) { - styleElement.appendChild(document.createTextNode(content)); + return styleElement; } - document.head.appendChild(styleElement); - return styleElement; } \ No newline at end of file