Skip to content

Commit

Permalink
refactor(layout): use single style tag for dynamically-created media …
Browse files Browse the repository at this point in the history
…queries (#10946)
  • Loading branch information
crisbeto authored and josephperrott committed May 1, 2018
1 parent 22524e3 commit 7d2c31e
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 25 deletions.
18 changes: 13 additions & 5 deletions src/cdk/layout/media-matcher.spec.ts
Expand Up @@ -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));
});
}
}));
});
42 changes: 22 additions & 20 deletions src/cdk/layout/media-matcher.ts
Expand Up @@ -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'})
Expand Down Expand Up @@ -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');

style.setAttribute('type', 'text/css');
if (!style.sheet) {
const cssText = `@media ${query} {.fx-query-test{ }}`;
style.appendChild(document.createTextNode(cssText));
}
if (mediaQueriesForWebkitCompatibility.has(query)) {
return;
}

document.getElementsByTagName('head')[0].appendChild(style);
try {
if (!mediaQueryStyleNode) {
mediaQueryStyleNode = document.createElement('style');
mediaQueryStyleNode.setAttribute('type', 'text/css');
document.head.appendChild(mediaQueryStyleNode);
}

// Store in private global registry
styleElementForWebkitCompatibility.set(query, style);
} catch (e) {
console.error(e);
if (mediaQueryStyleNode.sheet) {
(mediaQueryStyleNode.sheet as CSSStyleSheet)
.insertRule(`@media ${query} {.fx-query-test{ }}`, 0);
mediaQueriesForWebkitCompatibility.add(query);
}
} catch (e) {
console.error(e);
}
}

Expand Down

0 comments on commit 7d2c31e

Please sign in to comment.