JavaScript: (How) can one retrieve external CSS rules in Chrome's DevTools? - javascript

I'm working on a little Chrome extension that extends its DevTools. For this, I need to acquire all of the defined CSS selectors for the currently selected element ($0).
I know each item within document.styleSheets exposes all of the necessary data through cssRules. This would be perfect, but unfortunately CORS seems to throw a spanner in the works. For external stylesheets, cssRules returns null.
Is it possible to access this data without resorting to a hackish solution, e.g. downloading the stylesheet and inserting it into a style tag? I ask because Chrome itself seems to do so in its Styles sidebar panel, but I can't find much information on the matter.
Thanks!

I think I've figured it out. It just took a bit more digging in the documentation.
The inspectedWindow API exposes getResources, allowing you to fetch all of the resources within the inspected window. This includes type and functionality for fetching its content.
Injecting this content into a style tag allows you to access the CSS rules via document.styleSheets. This is ideal because my sidebar pane is encapsulated within a shadow DOM, allowing me to know exactly which stylesheets have been injected.
chrome.devtools.inspectedWindow.getResources(function(resources) {
for (var i = 0; i < resources.length; i++) {
if (resources[i].type != 'stylesheet') {
continue;
}
// inject the resource into the shadow DOM
// this allows us to freely access all CSS rules CORS-free
resources[i].getContent(function(content) {
var style = document.createElement('style');
style.textContent = content;
document.body.appendChild(style);
});
}
});

Related

Can I use javascript to change individual page css entries (as opposed to swapping stylesheets)?

I know it's possible to change css attributes on elements on the current page:
$('.changeMyStyle').css("color", "#FF0000");
But this won't affect new elements added after the change is made.
I know it's possible to remove, add, or swap out css stylesheets to re-style a page after it's been loaded:
$('link.swappableStylesheet').attr('href', 'path/to/new/style.css');
But this is a poor solution for changing one or two attributes, especially to programmatically-determined values (such as changing color from a colorpicker).
I could probably grab a stylesheet's raw data, search it, and modify it:
var sheet= document.styleSheets[0];
var rules= 'cssRules' in sheet? sheet.cssRules : sheet.rules; // IE compatibility
rules[0].style.padding= '0.32em 2em';
// assumes the first entry in the first stylesheet is the one you want to modify.
// if it's not, you have to search to find the exact selector you're looking for
// and pray it's not in a slightly different order
But that's also a poor solution and requires IE-compatibility hacks.
This linked answer also suggests appending another <style> element and adding css there. That could work for narrow cases, but it's still not ideal (and the answer is 5 years old, so new tools may be available now).
Is there a way to alter the page's css at a selector & attribute level instead of stylesheet level or DOM element level? jQuery and vanilla javascript solutions both welcome, as well as libraries designed to do this specifically. Ideally I'd like something that's as easy and versatile as
$(document).stylesheet('.arbitraryCssSelector.Here').put('color', '#FF0000');
...where .stylesheet('.Here.arbitraryCssSelector') would modify the exact same style entry.
Even Chrome's dev tools just modifies the stylesheet it's using when you make modifications or add new rules. There's not currently a way around it, but you can keep a dedicated stylesheet at the bottom of the page that you update with the newest rules. If it's empty or contains invalid rules it will just fall back to the current stylesheet. If any library exists out there this is how it would do it, and it's very little code.
I think the key to keeping it uncluttered is to simply keep overwriting one stylesheet instead of adding new stylesheets to the DOM.
document.getElementById("dynamic-color").addEventListener("input", function () {
document.getElementById("dynamic-styles").innerHTML = "label { color: " + this.value + " }";
});
label {
color: blue;
}
<label for="#dynamic-color">Change the label's color!</label>
<input id="dynamic-color" />
<style id="dynamic-styles"></style>

How to determine whether a linked resource loads successfully

I'm using the following JavaScript to dynamically load stylesheets:
function set_stylesheet(name) {
var link = document.getElementById('userstylelink');
link.href = link.href.replace(/[^\/]+\.css$/, name + '.css');
}
Is there any way to determine whether the new CSS file is loaded successfully? If it fails, I'd like to be able to apply a default stylesheet.
You might want to see my answer to another similar question here:
Detect and log when external JavaScript or CSS resources fail to load
Basically, you can add an onload callback to see if the file was loaded. (If you load via JS of course)
The simplest way is to check styleSheet.cssText property of the link element after a new href was assigned.
function set_stylesheet(name) {
var link = document.getElementById('userstylelink');
link.href = link.href.replace(/[^\/]+\.css$/, name + '.css');
if ( link.styleSheet.cssText ) {
//if not empty string, it was loaded
}
else {
link.href = "default.css";
}
Alternatively there is "onerror" event which fires when the resource fails to load after href assigned.
Ideally, you would load them all at the beginning and then switch between then.
http://www.alistapart.com/articles/alternate/
http://www.thesitewizard.com/javascripts/change-style-sheets.shtml
That doesn't really answer the question, but I think this way is preferred.
I'd recommend loading via ajax, checking the response as the user pimvbd mentions, but also place a dummy rule at the end of your stylesheet that styles a hidden element with a declaration you can check. For example, give a hidden div border: 987px and check to see if the hidden div's border is in fact 987px. Yes, it introduces a dependency on that style and that element. I've had endless discussions on this with many people, and there's not really a better way (yet). Hopefully s get some attention in browser releases in the near future...
There is a solution that requires no javascript or detection of whether the stylesheet loaded.
It seems you could also apply your default style with a built-in style sheet and then have the dynamically loaded stylesheet override the defaults. If the new stylesheet doesn't load, the default is already loaded and in place, nothing further to do. If the new stylesheet does load, it just overrides the default values and shows the new style.

Javascript CSS visability

It seems that javascript only can ready inline css if i want to check if element is display:hidden getting it with:
el.style.display
But how to check if display:none is placed inside external CSS file?
You'll need to access the computed style of the element with getComputedStyle (standards-compatible browsers) or currentStyle (IE). Google for those terms for examples or use a framework such as jQuery, which provide wrappers for that.
It might be the backwards way and not helpful in this case, but anyway:
If you want to check out what's in the loaded CSS files, you can get the loaded stylesheets with var sheets = document.styleSheets;, and access the first one with sheets[0];
Then, get the rules from it:
var rules = sheets[0].cssRules ? sheets[0].cssRules : sheets[0].rules;
Then loop through the rules to check them out:
var rule, selector;
for (var idx=0, len=rules.length; idx<len; ++idx) {
rule = rules[idx];
selector = rule.selectorText;
if (!selector) {continue;}
console.log(selector+' => '+ rule.style.cssText);
}
This is more or less straight from David Flanagan's great book "Javascript, the Definitive Guide (5th ed.)"
if(el.style.display=='none') will work regardless of where the CSS is defined.

In firefox, how can I change an existing CSS rule

In firefox, I have the following fragment in my .css file
tree (negative){ font-size: 120%; color: green;}
Using javascript, how do I change the rule, to set the color to red?
NOTE:
I do not want to change the element.
I want to change the rule.
Please do not answer with something like
...
element.style.color = 'red';
What you're looking for is the document.styleSheets property, through which you can access your css rules and manipulate them. Most browsers have this property, however the interface is slightly different for IE.
For example, try pasting the following in FF for this page and pressing enter:
javascript:alert(document.styleSheets[0].cssRules[1].cssText)
For me that yields the string "body { line-height: 1; }". There are methods/properties that allow you to manipulate the rules.
Here's a library that abstracts this interface for you (cross-browser): http://code.google.com/p/sheetup/
function changeCSSRule (stylesheetID, selectorName, replacementRules) {
var i, theStylesheet = document.getElementById(stylesheetID).sheet,
thecss = (theStylesheet.cssRules) ? theStylesheet.cssRules : theStylesheet.rules;
for(i=0; i < thecss.length; i++){
if(thecss[i].selectorText == selectorName) {
thecss[i].style.cssText = replacementRules;
}
}
};
You can change CSS rules in style sheets through the CSS Object Model (currently known as DOM Level 2 Style). However, if you literally have "tree (negative)" in your style sheet that rule will be dropped and not appear in the Object Model at all.
As there is no HTML element tree I am going to assume that tree is the id or class of another element.
You would first retrieve the DOM element by id:
var tree = document.getElementById("tree");
Now tree represents your DOM element and you can manipulate it any way you like:
tree.style.color = "red";
Here is a great reference for mapping css properties to their javascript equivalent.
I'm not sure you can do actual class/selector overrides. You would need to target each element that used the .tree class and set the CSS. The quickest and easiest way would be through jQuery (or another similar framework):
$('.tree').each(function() { this.style.color = "red"; });
You could even use the built-in CSS functions:
$('.tree').css('color', 'red');
(I did it the first way to show you how standard JS would do it. The $(...) part is jQuery for selecting all elements with the .tree class. If you're not using jQuery, you'd need alternative code.)
If tree is an ID, not a class (there should only be one on the page) so using getElementById should be fine. Your code should look like the other answer.
for( var i in document.getElementsByTagName("tree") ){
document.getElementsByTagName("tree")[i].style.color = "red";
}
As I said in another answer's comment, I've never seen this done how you want. I've only ever targeted elements the same way as the CSS renderer would and changed each element style.
I did see this though: jQuery.Rule
It sounds like it does what you want but the demo causes my browser to flip out a bit. I'd invite you to look at the source to see it really does do what you want, and if you want to use it without jQ, use it as a starting point.
Edit: yes this should work. It works by appending another <style> tag to the page and writing out your overrides within. It's fairly simple to follow if you wanted to port it to plain JS.
For debugging, you can use Firebug to change the CSS rules on-the-fly.
If you want to change the rendered css rules from one page request to the next then some sort of server-side scripting will be required. Otherwise the original style sheet would simply reload at the next page request.
If you want to use an event on the first page to force the server-side action then you can use AJAX to actually change the CSS rule for the user.
"I want to change the rule so that
when I navigate to the next page, I
don't have to make all the changes
again."
It sounds like what you might want then is a remote request ("ajax") back to the server with the request you want to make, and generate a dynamic stylesheet which is sent back to the client?
How/why is this Firefox specific?
I want to change the rule so that when I navigate to the next page, I don't have to make all the changes again.
There are two approaches I can think of here. Namely client side and/or server side.
Client side:
Store the theme setting into cookies and load them up next time by javascript.
Server side:
If your site have an login system, you may also store the user preference into the database and generate the webpages with this inforamtion in mind next time on.
Utimately, you are still writing things like element.style.color =. But, they should get what you want.

How can one detect via Javascript if a print stylesheet is in effect?

I would like to have alternate behavior during a print stylesheet on a web page. Something along the lines of:
If this page is being printed, don't
bother calling SWFObject to summon an
.swf into existence. Just leave the
HTML that the Flash will replace.
I've tried things like setting a known element to a known style that exists for the screen but not for the print stylesheet. But getting a "style" via Javascript doesn't get a computed style.
Summary: In a cross-browser way, is it possible to tell which stylesheet is in effect?
It sounds like you're confused that print style-sheets are used when you view a printer-friendly page, but that is not the case. A print style sheet isn't applied until the user actually sends the page to the printer. At this point, any javascript that is going to run has already finished.
What you want to do is put your SWFObject inside a div container, and have the container styled as display:none; for the print media.
You could use JavaScript to access the stylesheets in the document and then check if the 'Print' stylesheet is active. Once you determined which CSS is active then you could manage your content.
The getActiveStyleSheet function would looks something like this:
function getActiveStyleSheet()
{
var i, a;
for (i = 0; (a = document.getElementsByTagName("link")[i]); i++)
{
if (a.getAttribute("rel").indexOf("style") != -1
&& a.getAttribute("title")
&& !a.disabled)
return a.getAttribute("title");
}
return null;
}
You can find the code here: http://www.alistapart.com/articles/alternate/.

Categories

Resources