feat: add insertInto option (#135)
parent
71e0908536
commit
663686884b
|
@ -66,7 +66,10 @@ Note: Behavior is undefined when `unuse`/`unref` is called more often than `use`
|
|||
|
||||
#### `insertAt`
|
||||
|
||||
By default, the style-loader appends `<style>` elements to the end of the `<head>` tag of the page. This will cause CSS created by the loader to take priority over CSS already present in the document head. To insert style elements at the beginning of the head, set this query parameter to 'top', e.g. `require('../style.css?insertAt=top')`.
|
||||
By default, the style-loader appends `<style>` elements to the end of the style target, which is the `<head>` tag of the page unless specified by `insertInto`. This will cause CSS created by the loader to take priority over CSS already present in the target. To insert style elements at the beginning of the target, set this query parameter to 'top', e.g. `require('../style.css?insertAt=top')`.
|
||||
|
||||
#### `insertInto`
|
||||
By default, the style-loader inserts the `<style>` elements into the `<head>` tag of the page. If you want the tags to be inserted somewhere else, e.g. into a [ShadowRoot](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot), you can specify a CSS selector for that element here, e.g. `require('../style.css?insertInto=#host::shadow>#root')`.
|
||||
|
||||
#### `singleton`
|
||||
|
||||
|
|
30
addStyles.js
30
addStyles.js
|
@ -13,8 +13,16 @@ var stylesInDom = {},
|
|||
isOldIE = memoize(function() {
|
||||
return /msie [6-9]\b/.test(self.navigator.userAgent.toLowerCase());
|
||||
}),
|
||||
getHeadElement = memoize(function () {
|
||||
return document.head || document.getElementsByTagName("head")[0];
|
||||
getElement = (function(fn) {
|
||||
var memo = {};
|
||||
return function(selector) {
|
||||
if (typeof memo[selector] === "undefined") {
|
||||
memo[selector] = fn.call(this, selector);
|
||||
}
|
||||
return memo[selector]
|
||||
};
|
||||
})(function (styleTarget) {
|
||||
return document.querySelector(styleTarget)
|
||||
}),
|
||||
singletonElement = null,
|
||||
singletonCounter = 0,
|
||||
|
@ -33,7 +41,10 @@ module.exports = function(list, options) {
|
|||
// tags it will allow on a page
|
||||
if (typeof options.singleton === "undefined") options.singleton = isOldIE();
|
||||
|
||||
// By default, add <style> tags to the bottom of <head>.
|
||||
// By default, add <style> tags to the <head> element
|
||||
if (typeof options.insertInto === "undefined") options.insertInto = "head";
|
||||
|
||||
// By default, add <style> tags to the bottom of the target
|
||||
if (typeof options.insertAt === "undefined") options.insertAt = "bottom";
|
||||
|
||||
var styles = listToStyles(list);
|
||||
|
@ -103,19 +114,22 @@ function listToStyles(list) {
|
|||
}
|
||||
|
||||
function insertStyleElement(options, styleElement) {
|
||||
var head = getHeadElement();
|
||||
var styleTarget = getElement(options.insertInto)
|
||||
if (!styleTarget) {
|
||||
throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
|
||||
}
|
||||
var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
|
||||
if (options.insertAt === "top") {
|
||||
if(!lastStyleElementInsertedAtTop) {
|
||||
head.insertBefore(styleElement, head.firstChild);
|
||||
styleTarget.insertBefore(styleElement, styleTarget.firstChild);
|
||||
} else if(lastStyleElementInsertedAtTop.nextSibling) {
|
||||
head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
|
||||
styleTarget.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
|
||||
} else {
|
||||
head.appendChild(styleElement);
|
||||
styleTarget.appendChild(styleElement);
|
||||
}
|
||||
styleElementsInsertedAtTop.push(styleElement);
|
||||
} else if (options.insertAt === "bottom") {
|
||||
head.appendChild(styleElement);
|
||||
styleTarget.appendChild(styleElement);
|
||||
} else {
|
||||
throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ describe("basic tests", function() {
|
|||
localScopedCss = ":local(.className) { background: red; }",
|
||||
requiredStyle = `<style type="text/css">${requiredCss}</style>`,
|
||||
existingStyle = "<style>.existing { color: yellow }</style>",
|
||||
checkValue = '<div class="check">check</div>',
|
||||
rootDir = path.resolve(__dirname + "/../") + "/",
|
||||
jsdomHtml = [
|
||||
"<html>",
|
||||
|
@ -21,6 +22,9 @@ describe("basic tests", function() {
|
|||
existingStyle,
|
||||
"</head>",
|
||||
"<body>",
|
||||
"<div class='target'>",
|
||||
checkValue,
|
||||
"</div>",
|
||||
"</body>",
|
||||
"</html>"
|
||||
].join("\n");
|
||||
|
@ -83,6 +87,15 @@ describe("basic tests", function() {
|
|||
runCompilerTest(expected, done);
|
||||
}); // it insert at top
|
||||
|
||||
it("insert into", function(done) {
|
||||
let selector = "div.target";
|
||||
styleLoaderOptions.insertInto = selector;
|
||||
|
||||
let expected = [checkValue, requiredStyle].join("\n");
|
||||
|
||||
runCompilerTest(expected, done, undefined, selector);
|
||||
}); // it insert into
|
||||
|
||||
it("singleton", function(done) {
|
||||
// Setup
|
||||
styleLoaderOptions.singleton = true;
|
||||
|
@ -218,4 +231,4 @@ describe("basic tests", function() {
|
|||
runCompilerTest(expected, done, function() { return this.css.locals.className; });
|
||||
}); // it local scope
|
||||
|
||||
}); // describe
|
||||
}); // describe
|
||||
|
|
|
@ -57,7 +57,8 @@ module.exports = {
|
|||
* @param {function} done - Async callback from Mocha.
|
||||
* @param {function} actual - Executed in the context of jsdom window, should return a string to compare to.
|
||||
*/
|
||||
runCompilerTest: function(expected, done, actual) {
|
||||
runCompilerTest: function(expected, done, actual, selector) {
|
||||
selector = selector || "head"
|
||||
compiler.run(function(err, stats) {
|
||||
if (stats.compilation.errors.length) {
|
||||
throw new Error(stats.compilation.errors);
|
||||
|
@ -73,7 +74,7 @@ module.exports = {
|
|||
if (typeof actual === 'function') {
|
||||
assert.equal(actual.apply(window), expected);
|
||||
} else {
|
||||
assert.equal(window.document.head.innerHTML.trim(), expected);
|
||||
assert.equal(window.document.querySelector(selector).innerHTML.trim(), expected);
|
||||
}
|
||||
// free memory associated with the window
|
||||
window.close();
|
||||
|
@ -83,4 +84,4 @@ module.exports = {
|
|||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue