Listing known CSS classes using Javascript - javascript

I'm trying to find a good way to collect the names of classes defined in the stylesheets included with a given document. I know about document.StyleSheetList but it doesn't seem like it'd be easy to parse. What I'm looking for is something like, for a stylesheet document such as:
.my_class {
background: #fff000;
}
.second_class {
color: #000000;
}
I could extract an array like ["my_class", "second_class"]. This obviously assumes the favorable scenario of a fully loaded dom and stylesheets.
I've been looking everywhere for a good way to do something like this and so far, have made little progress. Does anyone have any idea about how to pull this off? Thanks!

This will show all rules defined in the stylesheets.
var allRules = [];
var sSheetList = document.styleSheets;
for (var sSheet = 0; sSheet < sSheetList.length; sSheet++)
{
var ruleList = document.styleSheets[sSheet].cssRules;
for (var rule = 0; rule < ruleList.length; rule ++)
{
allRules.push( ruleList[rule].selectorText );
}
}
The thing, though, is that it includes all rules regardless of being class or tag or id or whatever..
You will need to explain in more detail what you want to happen for non class rules (or combined rules)

You were on track with document.styleSheets (https://developer.mozilla.org/en/DOM/document.styleSheets)
https://developer.mozilla.org/en/DOM/stylesheet.cssRules
Here's a quick and dirty method to output all class selectorTexts to the console in Firefox + Firebug.
var currentSheet = null;
var i = 0;
var j = 0;
var ruleKey = null;
//loop through styleSheet(s)
for(i = 0; i<document.styleSheets.length; i++){
currentSheet = document.styleSheets[i];
///loop through css Rules
for(j = 0; j< currentSheet.cssRules.length; j++){
//log selectorText to the console (what you're looking for)
console.log(currentSheet.cssRules[j].selectorText);
//uncomment to output all of the cssRule contents
/*for(var ruleKey in currentSheet.cssRules[j] ){
console.log(ruleKey +': ' + currentSheet.cssRules[j][ruleKey ]);
}*/
}
}

This is probably not something you really want to be doing except as part of a refactoring process, but here is a function that should do what you want:
function getClasses() {
var classes = {};
// Extract the stylesheets
return Array.prototype.concat.apply([], Array.prototype.slice.call(document.styleSheets)
.map(function (sheet) {
if(null == sheet || null == sheet.cssRules) return;
// Extract the rules
return Array.prototype.concat.apply([], Array.prototype.slice.call(sheet.cssRules)
.map(function(rule) {
// Grab a list of classNames from each selector
return rule.selectorText.match(/\.[\w\-]+/g) || [];
})
);
})
).filter(function(name) {
// Reduce the list of classNames to a unique list
return !classes[name] && (classes[name] = true);
});
}

What about
.something .other_something?
Do you want a pool of classNames that exist? Or a pool of selectors?
Anyway, have you tried iterating through document.styleSheets[i].cssRules? It gives you the selector text. Parsing that with some regexp kungfu should be easier...
Do you need it to be crossbrowser?

You can accompish this with jQuery. Example would be
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
var allobjects = $("*")
});
</script>
Check out the jQuery website: http://api.jquery.com/all-selector/

Related

Make a html+css page standalone and static

I'm trying to make a web page "static", with all styles as inline.
For this I first save all computed styles, then remove all applied style (class and css files), check the difference with saved styles and add inline for missing styles.
This is my function:
var allstyles=null;
var allelms=null;
function FixHTML()
{
if (!allstyles)
{
console.log("remove scripts");
var elms=document.getElementsByTagName('SCRIPT');
for(var i=elms.length-1;i>-1;i--)
{
var elm=elms[i];
if (elm.tagName=="SCRIPT" && !elm.innerHTML.match(/FixHTML/))
{
elm.parentElement.removeChild(elm);
}
}
//sauvegarde des styles
console.log("save styles");
allstyles=[];
allelms=document.getElementsByTagName('*');
for(i=0;i<allelms.length;i++)
{
elm=allelms[i];
if (!elm.id)
elm.id="tmpid"+i;
var id=elm.id;
allstyles[id]=[];
var style=getStyleObject(elm);
for(var key in style)
{
allstyles[id][key]=style[key];
}
if (elm.className)
elm.className="";
}
console.log("delete stylesheet links");
elms=document.getElementsByTagName('LINK');
for(i=elms.length-1;i>-1;i--)
{
elm=elms[i];
console.log(elm.href);
if (elm.rel="stylesheet")
elm.href="nowhere";
}
}
setTimeout(RestoreClassStyles,2000);
}
function RestoreClassStyles()
{
console.log("restore class styles",allstyles);
allelms=document.getElementsByTagName('*');
for(var i=0;i<allelms.length;i++)
{
var elm=allelms[i];
var id=elm.id;
var style=getStyleObject(elm);
for(var key in allstyles[id])
{
if (allstyles[id][key]!=style[key])
{
console.log(key);
elm.style[key]=allstyles[id][key];
}
}
}
}
function getStyleObject(dom){
var style;
var returns = {};
if(window.getComputedStyle){
var camelize = function(a,b){
return b.toUpperCase();
};
style = window.getComputedStyle(dom, null);
for(var i = 0, l = style.length; i < l; i++){
var prop = style[i];
var camel = prop.replace(/\-([a-z])/g, camelize);
var val = style.getPropertyValue(prop);
returns[camel] = val;
};
return returns;
};
console.log("not found",elm);
return "";
}
It "works a bit" but some styles are incorrect so I need help to find what I'm missing in that code.
Working example: http://jsfiddle.net/owecfdr2/
Instead of using inline styles, I would convert the link elements to style elements. You can do that like this (I've stuck to ES5 because it looked like you were doing so in your code):
function linkToStyle(link) {
var css = [];
var sheet = link.sheet;
var rules = sheet.cssRules || sheet.rules;
for (var i = 0; i < rules.length; ++i) {
var rule = rules[i];
css.push(rule.cssText);
}
var style = document.createElement("style");
style.type = "text/css";
style.appendChild(
document.createTextNode(css.join("\r\n"))
);
return style;
}
document.querySelectorAll("link[rel=stylesheet]").forEach(function(link) {
var style = linkToStyle(link);
var parent = link.parentNode;
parent.insertBefore(style, link);
parent.removeChild(link);
});
That finds all the link elements with rel="stylesheet" and replaces them with style elements with the same CSS text, by getting the CSS stylesheet object from the link element, looping through its CSS rules, and using the cssText of each rule.
As misorude points out in a comment, any URLs in the linked CSS stylesheets are relative to the stylesheet, not the page linking to the style sheet, so if your stylesheets and page are not in the same place from a URL perspective (e.g., same directory), it gets a bit more complicated: you'll have to adjust the URLs to account for that. A simple version would just handle rules of type CSSRule.STYLE_RULE (which are CSSStyleRules), looping through the properties to find and adjust URLs. (If there are imports, it gets more complicated.)
That code relies on forEach on the NodeList returned by querySelectorAll. Most browsers have that now; this answer shows how to detect if the browser doesn't and polyfill it.

For loop within a for loop? - Javascript

newbie javascript question. I made sure to research as much as I could before posting here, I've tried many solutions but could be searching for the wrong thing.
I've attached an image below of the issue I have. I'm trying to retrieve everything in the dark blue boxes, but I can't identify those input tags as there is nothing unique about them, I can however identify their parent divs by the class 'f-active'. When the divs have that class they have been selected by the user which is what I am interested in.
My attempt so far
var divArray = document.querySelectorAll('div.add-filter.f-active');
var arr = [];
for(var i=0; i < divArray.length; i++){
var childArray = divArray[i].children;
// console.log(childArray);
for(var i=0; i < childArray.length; i++){
if(childArray[i].tagName == "INPUT"){
var catNameCollection = arr.push(childArray[i].name);
// console.log(catNameCollection);
}
}
}
I tried to use a for loop to get all the parents, then use another for loop to select the children (input tags) and then grab the name attribute, however it is just outputing numbers. I did originally try to create 'divArray' as document.querySelectorAll('div.add-filter.f-active').children, but this and then grab the name attribute in the for loop, but this didn't return anything at all.
Any help anyone could offer would be greatly appreciated, I'd love to know what I'm doing wrong.
Thank you!
Your i is same for both loops. Use:
var divArray = document.querySelectorAll('div.add-filter.f-active');
var arr = [];
for(var i=0; i < divArray.length; i++){
var childArray = divArray[i].children;
// console.log(childArray);
for(var k=0; k < childArray.length; k++){
if(childArray[k].tagName == "INPUT"){
var catNameCollection = arr.push(childArray[k].name);
// console.log(catNameCollection);
}
}
}
Classic for-loops usually aren't the best tool for iterating through DOM elements - they add a lot of clutter and are error-prone, especially when you have to nest them.
In your case it'd be simpler to instead modify your query to directly grab all input elements with a div.f-active parent, then extract the names by iterating through them with a forEach. For example (using ES6 or higher):
const arr = [];
// Get list of all <input> elements that have <div> element parents with class f-active.
const nodes = document.querySelectorAll('div.add-filter.f-active > input');
// Extract name from each input element matched by your selector.
nodes.forEach(node => arr.push(node.name));
Or if you're stuck using ES5:
var arr = [];
var nodes = document.querySelectorAll('div.add-filter.f-active > input');
nodes.forEach(function(node) {
arr.push(node.name);
});
Here's a quick JSFiddle I put together to demonstrate the concept for you. (You'll need to open the console to see the result)
Hopefully that helps :)

Apply javascript just in a div?

I have coded a few lines which extract the URL of all the img tags in the page.
Ok everything works well but I wonder if there is a way to apply my code in a special div in the page!!!?
I mean I want to get URL of img tags from a div not from the whole page body.
I hope that I explained clearly what I meant :)
Any solution would be appreciated
function getURL() {
var url = [];
var a = document.getElementsByTagName('img');
for (var i=0, j = a.length; i<j; i++)
{
if (/\.(jpg|jpeg|gif|png)$/im.test(a[i].getAttribute('src')))
{
url.push(a[i].getAttribute('src'));
}
}
document.write(url);
}
Replace document.getElementsByTagName('img') with yourElement.getElementsByTagName('img');
Change your function to accept a startElement parameter, then call the getElementsByTagName on the passed element
function getURL(startElement) {
var url = [];
var a = startElement.getElementsByTagName('img');
for (var i=0, i < a.length; i++)
{
if (/\.(jpg|jpeg|gif|png)$/im.test(a[i].getAttribute('src')))
{
url.push(a[i].getAttribute('src'));
}
}
return url; // return the result instead of writing it
}
Say you have this markup
<div id='myDiv'><img src='test.jpg'/></div>
You could then call
var urls = getUrl(document.getElementById('myDiv'));
Also I suggest not using document.write, open dev tools (usually F12) and use console.log instead,
console.log(urls);

How to get all HTML attributes which start with something (the attribute names, *not* their values!)

I would like to get all the elements/nodes in an HTML page which contain attributes that start with something (again, the attribute names start with something, not their values!). For example, TinyMCE has a tendency of adding custom attributes to the elements it saves, like "mce_style", "mce_href", "mce_bogus", etc. I would like to have something like the CSS3 selector for attribute values, [attr^="mce_"], but not for the values, the attribute names.
Of course, I can iterate through all DOM nodes and their attributes and check them one by one, but I was wondering whether there is a more efficient way.
Please don't give me TinyMCE-specific answers, I'm pretty sure there's a flag which would prevent TinyMCE for saving these attributes, but the question is generic.
here's a simple demo to find all elements that contain an attribute starting with mce_. might need some refinements.
function getMCE() {
var el, attr, i, j, arr = [],
reg = new RegExp('^mce_', 'i'), //case insensitive mce_ pattern
els = document.body.getElementsByTagName('*'); //get all tags in body
for (i = 0; i < els.length; i++) { //loop through all tags
el = els[i] //our current element
attr = el.attributes; //its attributes
dance: for (j = 0; j < attr.length; j++) { //loop through all attributes
if (reg.test(attr[j].name)) { //if an attribute starts with mce_
arr.push(el); //push to collection
break dance; //break this loop
}
}
}
return arr;
}
console.log(getMCE())​
Try this:
FUNCTIONS
//custom selector expression
$.extend($.expr[':'],{
attr:function(o,i,m){
var attrs=$.getAttrAll(o),re=m[3],found=false;
$.each(attrs,function(k,v){
if(new RegExp(re).test(v)) { return found=true;}
});
return found;
}
});
// get all atrributes of an element
$.getAttrAll=function(el){
var rect = [];
for (var i=0, attrs=el.attributes, len=attrs.length; i<len; i++){
rect.push(attrs.item(i).nodeName);
}
return rect;
};
`
USAGE
// calling custom selector expression :attr(regexp)
$(function(){
$('body').find(':attr("^mce_")').css({background:'yellow'});
});
HTML
<body>
<p mce_style="height:50px" id="x" data-hello="hello">selected</p>
<div not_mce_bogus="abc">not_mce_bogus</div>
<div mce_href="http://rahenrangan.com">selected</div>
<p>othrs</p>
</body>
One option, if you don't mind temporarily altering your DOM, is to extract your HTML into a string and search for the attributes via RegExp. When you find the attributes, you could append a "needle" in the DOM so that you can use jQuery to select the elements.
Here is a working concept (run with console open):
http://jsfiddle.net/skylar/N43Bm/
Code:
$.fn.extend({
findAttributes: function(attribute) {
var attributeFinder = new RegExp(attribute + '(.+)="', "gi");
var elementHTML = this.html().replace(attributeFinder, "data-needle='pin' "+attribute+"$1=\"");
this.html(elementHTML);
return this.find("[data-needle=pin]").removeAttr('data-needle');
}
});
console.log($("body").findAttributes('mce_'));
Note: my regexp is not great. You'll have to take better care than I have in this example.
Try this: (I tried putting * instead of a tag but it colored all the elements including those who do not have mce_style attribute as well)
a[mce_style] { color : red; }​
Demo : http://jsfiddle.net/Tcdmb/
More info : https://developer.mozilla.org/en/CSS/Attribute_selectors

Selecting elements without jQuery

I guess this will be voted down, as it doesn't contain enough jQuery, but here it goes :)
What is the most effective way to get the element(s) returned by the jQuery selector below using plain old javascript?
$('a[title="some title text here"]', top.document)
If you're using a modern browser, you could use this:
window.top.document.querySelectorAll('a[title="some title text here"]')
Not sure if it’s the most effective, but at least it works.
var links = top.document.getElementsByTagName('a');
var result = [];
var linkcount = links.length;
for ( var i = 0; i < linkcount; i++) {
if (links[i].getAttribute('title') === 'some title text here') {
result.push(links[i]);
}
}
Here is an example
var getElements = function(tagName, attribute, value, callback) {
var tags = window.document.getElementsByTagName(tagName);
for (var i=0; i < tags.length; i++) {
var tag = tags[i];
if (tag.getAttribute(attribute) == value) {
callback(tag);
}
};
};
getElements("a", "title", "PHP power player at Hettema & Bergsten. Click to learn more.", function(tag) {
console.log(tag);
});

Categories