During investigation of advantages and disadvantages of attaching CSS with <?xml-stylesheet>
processing instruction, I came upon some issues.
Suppose we have a simple XHTML document (which is delivered with application/xhtml+xml
MIME type and viewed in a Web browser):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>A sample XHTML document</title>
<script type="application/javascript" src="/script.js"></script>
</head>
<body>
<h1>A heading</h1>
</body>
</html>
Then we have an external CSS file (let it be named style.css
and put in root directory):
h1 { color: red; }
At first, in script.js
, I dynamically attach this CSS with a link
element:
const link = document.createElement('link');
Object.entries({rel: 'stylesheet', type: 'text/css', href: '/style.css'})
.forEach(([name, value]) => link.setAttribute(name, value));
document.head.appendChild(link);
Then the script is waiting until the stylesheet finishes loading and reaches it through sheet
property:
link.addEventListener('load', function() {
const stylesheet = link.sheet;
});
After this, the script can manipulate this stylesheet, for example:
stylesheet.cssRules.item(0).style.color = 'green'; // modify an existing rule
stylesheet.insertRule('body { background: #ffc; }', 1); // insert a new rule
But now, I cannot figure out whether the same manipulations are possible if a stylesheet is attached with <?xml-stylesheet>
processing instruction:
const pi = document.createProcessingInstruction('xml-stylesheet',
'href="/style.css" type="text/css"');
document.insertBefore(pi, document.documentElement);
First, PI seem not to have load
event, so the script cannot know when the stylesheet is ready. Second, there is nothing like sheet
property, so you cannot call pi.sheet
to reach the stylesheet.
Is there any way to overcome these difficulties and to get from the script to the stylesheet associated with <?xml-stylesheet>
PI?
First, PI seem not to have load event, so the script cannot know when the stylesheet is ready.
You can use PerformanceObserver
to check for requested and loaded resources. Iterates nodes of document
, check for .nodeType
7
or .nodeType
8
, as ProcessingInstruction
node could have comment
.nodeType
. Get "resource"
property from performance entries. Parse .nodeValue
of filtered node for URL at href="URL"
, check if value is equal to "resource"
of performance entry, then check if .styleSheet
.href
value is equal to parsed URL, and if parsed URL is equal to performance entry "resource"
property value. If true
, iterate .cssRules
or .rules
of the styleSheet
loaded at ProcessingInstruction
node.
window.onload = () => {
let resource;
const observer = new PerformanceObserver((list, obj) => {
for (let entry of list.getEntries()) {
for (let [key, prop] of Object.entries(entry.toJSON())) {
if (key === "name") {
resource = prop;
var nodes = document.childNodes;
_nodes: for (let node of nodes) {
if (node.nodeType === 7 || node.nodeType === 8
&& node.nodeValue === pi.nodeValue) {
let url = node.baseURI
+ node.nodeValue.match(/[^href="][a-z0-9/.]+/i)[0];
if (url === resource) {
observer.disconnect();
// use `setTimeout` here for
// low RAM, busy CPU, many processes running
let stylesheets = node.rootNode.styleSheets;
for (let xmlstyle of stylesheets) {
if (xmlstyle.href === url && url === resource) {
let rules = (xmlstyle["cssRules"] || xmlstyle["rules"]);
for (let rule of rules) {
// do stuff
console.log(rule, rule.cssText, rule.style, xmlstyle);
break _nodes;
}
}
}
}
}
}
}
}
}
});
observer.observe({
entryTypes: ["resource"]
});
const pi = document.createProcessingInstruction('xml-stylesheet',
'href="style.css" type="text/css"');
document.insertBefore(pi, document.documentElement);
}
plnkr http://plnkr.co/edit/uXfSzu0dMDCOfZbsdA7n?p=preview
You can also use MutationObserver
, setTimeout()
to handle
low RAM, busy CPU, many processes running
window.onload = function() {
let observer = new MutationObserver(function(mutations) {
console.log(mutations)
for (let mutation of mutations) {
for (let node of mutation.addedNodes) {
if (node.nodeName === "xml-stylesheet") {
let url = node.baseURI
+ node.nodeValue.match(/[^href="][a-z0-9/.]+/i)[0];
setTimeout(function() {
for (let style of document.styleSheets) {
if (style.href === url) {
observer.disconnect();
// do stuff
console.log(style)
}
}
// adjust `duration` to compensate for device
// low RAM, busy CPU, many processes running
}, 500)
}
}
}
});
observer.observe(document, {
childList: true
});
const pi = document.createProcessingInstruction('xml-stylesheet',
'href="style.css" type="text/css"');
document.insertBefore(pi, document.documentElement);
}
plnkr http://plnkr.co/edit/AI4QZiBUx6f1Kmc5qNG9?p=preview
Alternatively, use XMLHttpRequest()
or fetch()
to request .css
file, create and append <style>
element to document
, do stuff with response text, set .textContent
of style
element to adjusted css
text.
来源:https://stackoverflow.com/questions/41497274/handling-xml-stylesheet-similar-to-link-rel-stylesheet