-
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
Conversation
CLA signature looks good 👍 |
src/generate.js
Outdated
@@ -94,7 +94,7 @@ export const defaultSelectorHandlers = [ | |||
} | |||
// Generate the styles normally, and then wrap them in the media query. | |||
const generated = generateSubtreeStyles(baseSelector); | |||
return `${selector}{${generated}}`; | |||
return [`${selector}{${generated}}`]; |
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 wonder if we actually need to create a new array here or if we can continue returning a string and handle it properly elsewhere. Also, do we need to change pseudoSelectors
above to also return an array?
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.
generateSubtreeStyles
should have the same return type as generateCSS
so pseudoSelectors
should already return an array. Would it make sense to make the Flow type for generateSubtreeStyles
more specific?
We could have generateCSS
handle a string being returned here, but we never want a selector handler to return strings containing multiple rules, as insertRule
expects a single rule to be passed. My thought here was that selector handlers should always return an array of strings with one rule per string.
This would be a breaking change to the current API, so perhaps we could, say, wrap strings returned by a selector handler in a @media all {...}
query and warn about that return type being deprecated. Or just bump by a major version.
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.
Ah, I see. The alternative would be to do something like this on line 173:
if (Array.isArray(result)) {
generatedStyles.push(...result);
} else {
generatedStyles.push(result);
}
I think making the breaking change is okay. @xymostech do you have any thoughts on this?
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.
Also, yes I think it would be good to improve the flow types where we can.
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.
Hmmm. I think the funky @media all{}
with a warning solution sounds the best to me, but I'm not that attached to it. I don't know how many people are using extensions right now, but I'd prefer if we didn't break it already?
(and as a ping, the psuedoSelectors
function above needs to be changed too, right?)
src/inject.js
Outdated
if (sheet.insertRule) { | ||
cssContents.forEach((rule) => { | ||
try { | ||
sheet.insertRule(rule, sheet.cssRules.length); |
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.
It might be worth moving this out into a variable that we increment, to avoid having to read this value every time.
let numberOfRules = sheet.cssRules.length;
cssContents.forEach((rule) => {
try {
sheet.insertRule(rule, numberOfRules);
numberOfRules += 1;
} catch(e) { ... }
});
src/inject.js
Outdated
// $FlowFixMe: legacy Internet Explorer compatibility | ||
styleTag.styleSheet.cssText += cssContents; | ||
if (sheet.insertRule) { | ||
cssContents.forEach((rule) => { |
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 wonder if we should make this a regular for loop for perf
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.
Will check how this affects performance.
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.
Ran both versions through your benchmark several times. There was no noticeable difference between the forEach
version and the for
loop version, but a small speedup when storing sheet.cssRules.length
in a variable.
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.
Ahh! Thank you so much for doing this! I've been meaning to get this out for a while but haven't had the time!
I left a bunch of comments about things to look at! Overall though, this change looks great. :)
src/inject.js
Outdated
const currentStyleTag = styleTag; | ||
cssContents.forEach((rule) => { | ||
currentStyleTag.innerText = (currentStyleTag.innerText || '') + rule; | ||
}); |
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.
We probably want to batch update this, like currentStyleTag.innerText = (currentStyleTag.innerText || '') + cssContents.join('');
.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
<3
|
||
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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
insertRule
is supported on ≥ IE 9. I tested it on IE 9 and AFAICT it worked just fine. What browsers does Aphrodite guarantee support for? The innerText
fallback should work for browsers without insertRule
, but I just want to get idea of how far back I should test this.
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.
Testing in IE 9 sounds good enough to me! Thanks!
src/inject.js
Outdated
@@ -137,7 +146,7 @@ const stringHandlers = { | |||
let alreadyInjected = {}; | |||
|
|||
// This is the buffer of styles which have not yet been flushed. | |||
let injectionBuffer = ""; | |||
let injectionBuffer = []; |
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.
Could you give this a : string[]
type to help flow make sure that these strings can't be null?
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.
ping on this?
src/generate.js
Outdated
@@ -94,7 +94,7 @@ export const defaultSelectorHandlers = [ | |||
} | |||
// Generate the styles normally, and then wrap them in the media query. | |||
const generated = generateSubtreeStyles(baseSelector); | |||
return `${selector}{${generated}}`; | |||
return [`${selector}{${generated}}`]; |
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.
Hmmm. I think the funky @media all{}
with a warning solution sounds the best to me, but I'm not that attached to it. I don't know how many people are using extensions right now, but I'd prefer if we didn't break it already?
(and as a ping, the psuedoSelectors
function above needs to be changed too, right?)
src/inject.js
Outdated
|
||
// Inject a string of styles 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 = (cssContents /* : string[] */) => { |
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.
Could this argument name and docstring be updated?
@lencioni @xymostech PTAL, addressed comments and updated to use the |
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.
This looks great to me! I'll let @lencioni do the final approve and merge to make sure that their concerns were addressed.
src/inject.js
Outdated
@@ -137,7 +146,7 @@ const stringHandlers = { | |||
let alreadyInjected = {}; | |||
|
|||
// This is the buffer of styles which have not yet been flushed. | |||
let injectionBuffer = ""; | |||
let injectionBuffer = []; |
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.
ping on this?
'^': { | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
<3
I don't think I'll be able to look for a while so don't hold it up for me.
…On Tue, May 16, 2017, 4:41 PM Emily Eisenberg ***@***.***> wrote:
***@***.**** approved this pull request.
This looks great to me! I'll let @lencioni <https://github.com/lencioni>
do the final approve and merge to make sure that their concerns were
addressed.
------------------------------
In src/inject.js
<#240 (comment)>:
> @@ -137,7 +146,7 @@ const stringHandlers = {
let alreadyInjected = {};
// This is the buffer of styles which have not yet been flushed.
-let injectionBuffer = "";
+let injectionBuffer = [];
ping on this?
------------------------------
In tests/generate_test.js
<#240 (comment)>:
> +
+ 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',
+ },
+ }], ***@***.*** all {.foo::before{color:red;} .foo::after{color:red;}}'], [handler], {}, false);
<3
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#240 (review)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAL7zpm2o3F5qLNsuPJqkiHkHsSlXXzhks5r6jQOgaJpZM4NZoqT>
.
|
Hey @reklawnos @xymostech, any plans to merge this soon? |
Instead of appeding a new text node to a style tag or appending to the `cssText` property of the stylesheet (which broke media queries in IE as documented in Khan#238), use `insertRule` to add CSS rules to the `style` element. This change requires styles to be generated as an array of strings with one rule per string, rather than a single large string that contains multiple rules.
10b4772
to
5606f41
Compare
assertStylesInclude('0%{opacity:0;-webkit-transform:scale(0.75) translate3d(1px, 2px, 0);-ms-transform:scale(0.75) translate3d(1px, 2px, 0);transform:scale(0.75) translate3d(1px, 2px, 0);}'); | ||
assertStylesInclude('100%{opacity:1;-webkit-transform:scale(1) translate3d(1px, 2px, 0);-ms-transform:scale(1) translate3d(1px, 2px, 0);transform:scale(1) translate3d(1px, 2px, 0);}'); | ||
assertStylesInclude('animation-name:keyframe_d35t13'); | ||
assertStylesInclude('0% {opacity: 0; -webkit-transform: scale(0.75) translate3d(1px,2px,0); -ms-transform: scale(0.75) translate3d(1px,2px,0); transform: scale(0.75) translate3d(1px,2px,0);}'); |
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.
Looks like some of the spaces you removed here are still there in the test
I think this looks good. There is one test that has a minor failure on it. I'll follow up with a fix for that after merging this. Thanks for your work on this! |
'Returning a string containing multiple rules is deprecated.', | ||
handler, | ||
); | ||
generatedStyles.push(`@media all {${result}}`); |
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!
Instead of appending a new text node to a style tag or appending to the
cssText
property of the stylesheet (which broke media queries in IE as documented in #238), useinsertRule
to add CSS rules to thestyle
element.This change requires styles to be generated as an array of strings with one rule per string, rather than a single large string that contains multiple rules.
This was (at least provisionally) implemented in #83 and #157 and discussed in #76, but it didn't seem like those were going anywhere.