refactor(layout): use single style tag for dynamically-created media queries (#10946)

This commit is contained in:
Kristiyan Kostadinov
2018-05-01 10:32:17 -04:00
committed by Joey Perrott
parent 22524e3896
commit 7d2c31efaa
2 changed files with 36 additions and 26 deletions
+13 -5
View File
@@ -28,16 +28,24 @@ describe('MediaMatcher', () => {
expect(mediaMatcher.matchMedia('(max-width: 1px)').matches).toBeFalsy();
});
it('adds css rules for provided queries when the platform is webkit, otherwise adds nothing.',
it('should add CSS rules for provided queries when the platform is webkit',
inject([Platform], (platform: Platform) => {
let randomWidth = Math.random();
expect(document.head.textContent).not.toContain(randomWidth);
const randomWidth = `${Math.random()}px`;
expect(getStyleTagByString(randomWidth)).toBeFalsy();
mediaMatcher.matchMedia(`(width: ${randomWidth})`);
if (platform.WEBKIT) {
expect(document.head.textContent).toContain(randomWidth);
expect(getStyleTagByString(randomWidth)).toBeTruthy();
} else {
expect(document.head.textContent).not.toContain(randomWidth);
expect(getStyleTagByString(randomWidth)).toBeFalsy();
}
function getStyleTagByString(str: string): HTMLStyleElement | undefined {
return Array.from(document.head.querySelectorAll('style')).find(tag => {
const rules = tag.sheet ? Array.from((tag.sheet as CSSStyleSheet).cssRules) : [];
return !!rules.find(rule => rule.cssText.includes(str));
});
}
}));
});
+23 -21
View File
@@ -8,10 +8,11 @@
import {Injectable} from '@angular/core';
import {Platform} from '@angular/cdk/platform';
/**
* Global registry for all dynamically-created, injected style tags.
*/
const styleElementForWebkitCompatibility: Map<string, HTMLStyleElement> = new Map();
/** Global registry for all dynamically-created, injected media queries. */
const mediaQueriesForWebkitCompatibility: Set<string> = new Set<string>();
/** Style tag that holds all of the dynamically-created media queries. */
let mediaQueryStyleNode: HTMLStyleElement | undefined;
/** A utility for calling matchMedia queries. */
@Injectable({providedIn: 'root'})
@@ -42,27 +43,28 @@ export class MediaMatcher {
}
/**
* For Webkit engines that only trigger the MediaQueryListListener when there is at least one CSS
* selector for the respective media query.
* For Webkit engines that only trigger the MediaQueryListListener when
* there is at least one CSS selector for the respective media query.
*/
function createEmptyStyleRule(query: string) {
if (!styleElementForWebkitCompatibility.has(query)) {
try {
const style = document.createElement('style');
if (mediaQueriesForWebkitCompatibility.has(query)) {
return;
}
style.setAttribute('type', 'text/css');
if (!style.sheet) {
const cssText = `@media ${query} {.fx-query-test{ }}`;
style.appendChild(document.createTextNode(cssText));
}
document.getElementsByTagName('head')[0].appendChild(style);
// Store in private global registry
styleElementForWebkitCompatibility.set(query, style);
} catch (e) {
console.error(e);
try {
if (!mediaQueryStyleNode) {
mediaQueryStyleNode = document.createElement('style');
mediaQueryStyleNode.setAttribute('type', 'text/css');
document.head.appendChild(mediaQueryStyleNode);
}
if (mediaQueryStyleNode.sheet) {
(mediaQueryStyleNode.sheet as CSSStyleSheet)
.insertRule(`@media ${query} {.fx-query-test{ }}`, 0);
mediaQueriesForWebkitCompatibility.add(query);
}
} catch (e) {
console.error(e);
}
}