-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use insertRule
to inject styles
#240
Changes from all commits
56c3136
3805992
2b736a1
1cab4d2
60ef3bf
f39889b
5606f41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,17 +19,17 @@ type ProcessedStyleDefinitions = { | |
// inserted anything yet. We could find this each time using | ||
// `document.querySelector("style[data-aphrodite"])`, but holding onto it is | ||
// faster. | ||
let styleTag = null; | ||
let styleTag /* : ?HTMLStyleElement */ = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. <3 |
||
|
||
// Inject a string of styles into a <style> tag in the head of the document. This | ||
// Inject a set of rules into a <style> tag in the head of the document. This | ||
// will automatically create a style tag and then continue to use it for | ||
// multiple injections. It will also use a style tag with the `data-aphrodite` | ||
// tag on it if that exists in the DOM. This could be used for e.g. reusing the | ||
// same style tag that server-side rendering inserts. | ||
const injectStyleTag = (cssContents /* : string */) => { | ||
const injectStyleTag = (cssRules /* : string[] */) => { | ||
if (styleTag == null) { | ||
// Try to find a style tag with the `data-aphrodite` attribute first. | ||
styleTag = document.querySelector("style[data-aphrodite]"); | ||
styleTag = ((document.querySelector("style[data-aphrodite]") /* : any */) /* : ?HTMLStyleElement */); | ||
|
||
// If that doesn't work, generate a new style tag. | ||
if (styleTag == null) { | ||
|
@@ -44,12 +44,20 @@ const injectStyleTag = (cssContents /* : string */) => { | |
} | ||
} | ||
|
||
const sheet = ((styleTag.styleSheet || styleTag.sheet /* : any */) /* : CSSStyleSheet */); | ||
|
||
if (styleTag.styleSheet) { | ||
// $FlowFixMe: legacy Internet Explorer compatibility | ||
styleTag.styleSheet.cssText += cssContents; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you test this change in IE? Looks like this "IE compatibility" branch got changed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Testing in IE 9 sounds good enough to me! Thanks! |
||
if (sheet.insertRule) { | ||
let numRules = sheet.cssRules.length; | ||
cssRules.forEach((rule) => { | ||
try { | ||
sheet.insertRule(rule, numRules); | ||
numRules += 1; | ||
} catch(e) { | ||
// The selector for this rule wasn't compatible with the browser | ||
} | ||
}); | ||
} else { | ||
styleTag.appendChild(document.createTextNode(cssContents)); | ||
styleTag.innerText = (styleTag.innerText || '') + cssRules.join(''); | ||
} | ||
}; | ||
|
||
|
@@ -113,17 +121,17 @@ const stringHandlers = { | |
if (val instanceof OrderedElements) { | ||
val.forEach((valVal, valKey) => { | ||
finalVal += generateCSS( | ||
valKey, [valVal], selectorHandlers, stringHandlers, false); | ||
valKey, [valVal], selectorHandlers, stringHandlers, false).join(''); | ||
}); | ||
} else { | ||
Object.keys(val).forEach(key => { | ||
finalVal += generateCSS( | ||
key, [val[key]], selectorHandlers, stringHandlers, false); | ||
key, [val[key]], selectorHandlers, stringHandlers, false).join(''); | ||
}); | ||
} | ||
finalVal += '}'; | ||
|
||
injectGeneratedCSSOnce(name, finalVal); | ||
injectGeneratedCSSOnce(name, [finalVal]); | ||
|
||
return name; | ||
} else { | ||
|
@@ -137,7 +145,7 @@ const stringHandlers = { | |
let alreadyInjected = {}; | ||
|
||
// This is the buffer of styles which have not yet been flushed. | ||
let injectionBuffer = ""; | ||
let injectionBuffer /* : string[] */ = []; | ||
|
||
// A flag to tell if we are already buffering styles. This could happen either | ||
// because we scheduled a flush call already, so newly added styles will | ||
|
@@ -163,7 +171,7 @@ const injectGeneratedCSSOnce = (key, generatedCSS) => { | |
asap(flushToStyleTag); | ||
} | ||
|
||
injectionBuffer += generatedCSS; | ||
injectionBuffer.push(...generatedCSS); | ||
alreadyInjected[key] = true; | ||
} | ||
|
||
|
@@ -186,7 +194,7 @@ export const injectStyleOnce = ( | |
}; | ||
|
||
export const reset = () => { | ||
injectionBuffer = ""; | ||
injectionBuffer = []; | ||
alreadyInjected = {}; | ||
isBuffering = false; | ||
styleTag = null; | ||
|
@@ -200,17 +208,21 @@ export const startBuffering = () => { | |
isBuffering = true; | ||
}; | ||
|
||
export const flushToString = () => { | ||
const flushToArray = () => { | ||
isBuffering = false; | ||
const ret = injectionBuffer; | ||
injectionBuffer = ""; | ||
injectionBuffer = []; | ||
return ret; | ||
}; | ||
|
||
export const flushToString = () => { | ||
return flushToArray().join(''); | ||
}; | ||
|
||
export const flushToStyleTag = () => { | ||
const cssContent = flushToString(); | ||
if (cssContent.length > 0) { | ||
injectStyleTag(cssContent); | ||
const cssRules = flushToArray(); | ||
if (cssRules.length > 0) { | ||
injectStyleTag(cssRules); | ||
} | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,9 +106,10 @@ describe('generateCSS', () => { | |
stringHandlers = {}, useImportant = true) => { | ||
const actual = generateCSS(className, styleTypes, selectorHandlers, | ||
stringHandlers, useImportant); | ||
const expectedNormalized = expected.split('\n').map(x => x.trim()).join(''); | ||
const formatStyles = (styles) => styles.replace(/(;|{|})/g, '$1\n'); | ||
assert.equal( | ||
const expectedArray = [].concat(expected); | ||
const expectedNormalized = expectedArray.map(rule => rule.split('\n').map(x => x.trim()).join('')); | ||
const formatStyles = (styles) => styles.map(style => style.replace(/(;|{|})/g, '$1\n')).join(''); | ||
assert.deepEqual( | ||
actual, | ||
expectedNormalized, | ||
` | ||
|
@@ -273,17 +274,17 @@ ${formatStyles(actual)} | |
padding: 30, | ||
} | ||
} | ||
], ` | ||
@media (min-width: 200px){ | ||
], [ | ||
`@media (min-width: 200px){ | ||
.foo{ | ||
padding:20px !important; | ||
} | ||
} | ||
@media (min-width: 400px){ | ||
}`, | ||
`@media (min-width: 400px){ | ||
.foo{ | ||
padding:30px !important; | ||
} | ||
}`, defaultSelectorHandlers); | ||
}`], defaultSelectorHandlers); | ||
}); | ||
|
||
it('supports custom string handlers', () => { | ||
|
@@ -345,7 +346,24 @@ ${formatStyles(actual)} | |
color: 'red', | ||
}, | ||
color: 'blue', | ||
}], '.foo{color:blue;}.bar .foo{color:red;}', [handler], {}, false); | ||
}], ['.foo{color:blue;}','.bar .foo{color:red;}'], [handler], {}, false); | ||
}); | ||
|
||
it('supports selector handlers that return strings containing multiple rules', () => { | ||
const handler = (selector, baseSelector, callback) => { | ||
if (selector[0] !== '^') { | ||
return null; | ||
} | ||
const generatedBefore = callback(baseSelector + '::before'); | ||
const generatedAfter = callback(baseSelector + '::after'); | ||
return `${generatedBefore} ${generatedAfter}`; | ||
}; | ||
|
||
assertCSS('.foo', [{ | ||
'^': { | ||
color: 'red', | ||
}, | ||
}], ['@media all {.foo::before{color:red;} .foo::after{color:red;}}'], [handler], {}, false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. <3 |
||
}); | ||
|
||
it('correctly prefixes border-color transition properties', () => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this may be a semver major change, since it could theoretically cause some styles to stop working in browsers that don't support media queries. For IE, this applies to IE8 and older.
Practically speaking, however, I would be surprised if this actually breaks anything for anyone, so I am tempted to go with semver minor.
What do you think @xymostech?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just my 2c, but if I understand correctly, if someone was not using media queries before this change, and needed to support IE8 for some reason, then it seems like this should likely be considered a breaking change, as all styles would no longer work?
Is there a concern that bumping semver major would cause an unnecessary burden to consumers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think you are correct. My main concern is that I don't want to do a major release if there is no practical benefit to it, but I guess there's no way to know so probably best to stay on the safe side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if opening a new PR to bump the version of
inline-style-prefixer
/~https://github.com/rofrischmann/inline-style-prefixer/blob/master/Changelog.md, which contains a few bug fixes and reported 30% performance improvement since , along with noting some potential internal perf benefits would be of practical benefit?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that sounds great to me!