Skip to content

Commit 0cc58a7

Browse files
committed
Merge pull request #24 from diurnalist/feature/singleton-tag
Adds `singleton` option to allow re-using a single <style> element
2 parents 95bf145 + 1d35be2 commit 0cc58a7

File tree

4 files changed

+122
-38
lines changed

4 files changed

+122
-38
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ Styles are not added on require, but instead on call to `use`/`ref`. Styles are
3232

3333
Note: Behavior is undefined when `unuse`/`unref` is called more often than `use`/`ref`. Don't do that.
3434

35+
### Options
36+
37+
#### `singleton`
38+
39+
If defined, the style-loader will re-use a single `<style>` element, instead of adding/removing individual elements for each required module. **Note:** this option is on by default in IE9, which has strict limitations on the # of style tags allowed on a page.
40+
3541
## Recommended configuration
3642

3743
By convention the reference-counted API should be bound to `.useable.css` and the simple API to `.css` (similar to other file types, i. e. `.useable.less` and `.less`).

addStyles.js

+90-17
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,36 @@
22
MIT License http://www.opensource.org/licenses/mit-license.php
33
Author Tobias Koppers @sokra
44
*/
5-
var stylesInDom = {};
5+
var stylesInDom = {},
6+
memoize = function(fn) {
7+
var memo;
8+
return function () {
9+
if (typeof memo === "undefined") memo = fn.apply(this, arguments);
10+
return memo;
11+
};
12+
},
13+
isIE9 = memoize(function() {
14+
return /msie 9\b/.test(window.navigator.userAgent.toLowerCase());
15+
}),
16+
getHeadElement = memoize(function () {
17+
return document.head || document.getElementsByTagName("head")[0];
18+
}),
19+
singletonElement = null,
20+
singletonCounter = 0;
621

7-
module.exports = function(list) {
22+
module.exports = function(list, options) {
823
if(typeof DEBUG !== "undefined" && DEBUG) {
924
if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
1025
}
26+
27+
options = options || {};
28+
// Force single-tag solution on IE9, which has a hard limit on the # of <style>
29+
// tags it will allow on a page
30+
if (typeof options.singleton === "undefined") options.singleton = isIE9();
31+
1132
var styles = listToStyles(list);
12-
addStylesToDom(styles);
33+
addStylesToDom(styles, options);
34+
1335
return function update(newList) {
1436
var mayRemove = [];
1537
for(var i = 0; i < styles.length; i++) {
@@ -20,7 +42,7 @@ module.exports = function(list) {
2042
}
2143
if(newList) {
2244
var newStyles = listToStyles(newList);
23-
addStylesToDom(newStyles);
45+
addStylesToDom(newStyles, options);
2446
}
2547
for(var i = 0; i < mayRemove.length; i++) {
2648
var domStyle = mayRemove[i];
@@ -33,7 +55,7 @@ module.exports = function(list) {
3355
};
3456
}
3557

36-
function addStylesToDom(styles) {
58+
function addStylesToDom(styles, options) {
3759
for(var i = 0; i < styles.length; i++) {
3860
var item = styles[i];
3961
var domStyle = stylesInDom[item.id];
@@ -43,12 +65,12 @@ function addStylesToDom(styles) {
4365
domStyle.parts[j](item.parts[j]);
4466
}
4567
for(; j < item.parts.length; j++) {
46-
domStyle.parts.push(addStyle(item.parts[j]));
68+
domStyle.parts.push(addStyle(item.parts[j], options));
4769
}
4870
} else {
4971
var parts = [];
5072
for(var j = 0; j < item.parts.length; j++) {
51-
parts.push(addStyle(item.parts[j]));
73+
parts.push(addStyle(item.parts[j], options));
5274
}
5375
stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
5476
}
@@ -73,44 +95,95 @@ function listToStyles(list) {
7395
return styles;
7496
}
7597

76-
function addStyle(obj) {
98+
function createStyleElement() {
7799
var styleElement = document.createElement("style");
78-
var head = document.head || document.getElementsByTagName("head")[0];
100+
var head = getHeadElement();
79101
styleElement.type = "text/css";
80102
head.appendChild(styleElement);
81-
applyToTag(styleElement, obj);
82-
return function(newObj) {
103+
return styleElement;
104+
}
105+
106+
function addStyle(obj, options) {
107+
var styleElement, update, remove;
108+
109+
if (options.singleton) {
110+
var styleIndex = singletonCounter++;
111+
styleElement = singletonElement || (singletonElement = createStyleElement());
112+
update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
113+
remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
114+
} else {
115+
styleElement = createStyleElement();
116+
update = applyToTag.bind(null, styleElement);
117+
remove = function () {
118+
styleElement.parentNode.removeChild(styleElement);
119+
};
120+
}
121+
122+
update(obj);
123+
124+
return function updateStyle(newObj) {
83125
if(newObj) {
84126
if(newObj.css === obj.css && newObj.media === obj.media /*&& newObj.sourceMap === obj.sourceMap*/)
85127
return;
86-
applyToTag(styleElement, obj = newObj);
128+
update(obj = newObj);
87129
} else {
88-
head.removeChild(styleElement);
130+
remove();
89131
}
90132
};
91-
};
133+
}
134+
135+
function replaceText(source, id, replacement) {
136+
var boundaries = ["/** >>" + id + " **/", "/** " + id + "<< **/"];
137+
var start = source.lastIndexOf(boundaries[0]);
138+
var wrappedReplacement = replacement
139+
? (boundaries[0] + replacement + boundaries[1])
140+
: "";
141+
if (source.lastIndexOf(boundaries[0]) >= 0) {
142+
var end = source.lastIndexOf(boundaries[1]) + boundaries[1].length;
143+
return source.slice(0, start) + wrappedReplacement + source.slice(end);
144+
} else {
145+
return source + wrappedReplacement;
146+
}
147+
}
148+
149+
function applyToSingletonTag(styleElement, index, remove, obj) {
150+
var css = remove ? "" : obj.css;
151+
152+
if(styleElement.styleSheet) {
153+
styleElement.styleSheet.cssText = replaceText(styleElement.styleSheet.cssText, index, css);
154+
} else {
155+
var cssNode = document.createTextNode(css);
156+
var childNodes = styleElement.childNodes;
157+
if (childNodes[index]) styleElement.removeChild(childNodes[index]);
158+
if (childNodes.length) {
159+
styleElement.insertBefore(cssNode, childNodes[index]);
160+
} else {
161+
styleElement.appendChild(cssNode);
162+
}
163+
}
164+
}
92165

93166
function applyToTag(styleElement, obj) {
94167
var css = obj.css;
95168
var media = obj.media;
96169
// var sourceMap = obj.sourceMap;
97-
98170
// No browser support
99171
// if(sourceMap && typeof btoa === "function") {
100172
// try {
101173
// css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(JSON.stringify(sourceMap)) + " */";
102174
// } catch(e) {}
103175
// }
176+
104177
if(media) {
105178
styleElement.setAttribute("media", media)
106179
}
107-
if (styleElement.styleSheet) {
180+
181+
if(styleElement.styleSheet) {
108182
styleElement.styleSheet.cssText = css;
109183
} else {
110184
while(styleElement.firstChild) {
111185
styleElement.removeChild(styleElement.firstChild);
112186
}
113187
styleElement.appendChild(document.createTextNode(css));
114188
}
115-
116189
}

index.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
MIT License http://www.opensource.org/licenses/mit-license.php
33
Author Tobias Koppers @sokra
44
*/
5-
var path = require("path");
5+
var loaderUtils = require("loader-utils"),
6+
path = require("path");
67
module.exports = function() {};
78
module.exports.pitch = function(remainingRequest) {
89
this.cacheable && this.cacheable();
10+
var query = loaderUtils.parseQuery(this.query);
911
return [
1012
"// style-loader: Adds some css to the DOM by adding a <style> tag",
1113
"",
1214
"// load the styles",
1315
"var content = require(" + JSON.stringify("!!" + remainingRequest) + ");",
1416
"if(typeof content === 'string') content = [[module.id, content, '']];",
1517
"// add the styles to the DOM",
16-
"var update = require(" + JSON.stringify("!" + path.join(__dirname, "addStyles.js")) + ")(content);",
18+
"var update = require(" + JSON.stringify("!" + path.join(__dirname, "addStyles.js")) + ")(content, " + JSON.stringify(query) + ");",
1719
"// Hot Module Replacement",
1820
"if(module.hot) {",
1921
" // When the styles change, update the <style> tags",

package.json

+22-19
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
{
2-
"name": "style-loader",
3-
"version": "0.8.1",
4-
"author": "Tobias Koppers @sokra",
5-
"description": "style loader module for webpack",
6-
"devDependencies": {
7-
"css-loader": "~0.8.0"
8-
},
9-
"repository": {
10-
"type": "git",
11-
"url": "git@github.com:webpack/style-loader.git"
12-
},
13-
"licenses": [
14-
{
15-
"type": "MIT",
16-
"url": "http://www.opensource.org/licenses/mit-license.php"
17-
}
18-
]
19-
}
1+
{
2+
"name": "style-loader",
3+
"version": "0.8.1",
4+
"author": "Tobias Koppers @sokra",
5+
"description": "style loader module for webpack",
6+
"devDependencies": {
7+
"css-loader": "~0.8.0"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git@github.com:webpack/style-loader.git"
12+
},
13+
"licenses": [
14+
{
15+
"type": "MIT",
16+
"url": "http://www.opensource.org/licenses/mit-license.php"
17+
}
18+
],
19+
"dependencies": {
20+
"loader-utils": "^0.2.5"
21+
}
22+
}

0 commit comments

Comments
 (0)