edit
added a test with regexpr to solve the problem techfoobar pointed out
edit 2
corrected code for others hf :)
I'm looking for a way to get all static styles from a specified class. I don't want to append a new div and extract all possible values from it because values are often just computed...
Here are some related posts:
jQuery CSS plugin that returns computed style of element to pseudo clone that element?
Can jQuery get all CSS styles associated with an element?
http://quirksmode.org/dom/w3c_css.html
This is what i came up so far:
function getExternalCSS(className) {
var cssText = false;
$.each(document.styleSheets, function(key, value) {
$.each(value.cssRules, function(k, v) {
if(new RegExp(className, "i").test(v.selectorText)) {
cssText = v.cssText;
return false;
}
});
if(cssText !== false) {
return false;
}
});
return cssText;
}
css to browse:
.someClass {
style1...
}
.someOtherClass {
style2...
}
.myClassToFind {
style3...
}
usage:
getExternalCSS(".myClassToFind"); //note the . for class
The selectorText is returned correctly, but the if is never triggered. I already tried to parse to string etc. Any ideas?
Update
Check this demo: http://jsfiddle.net/XaB3T/1/
There were a couple of problems:
a) You need to return false from a $.each to stop the looping, its like break. This was not being done.
b) You were checking v.selectorText.toLowerCase() with '.myClass' which surely cannot match since '.myClass' is not all small case
Explanation for For counting in only those styles that will be applied at the end, you might need to do a lot more!
For CSS specificity rules, please refer to:
http://www.htmldog.com/guides/cssadvanced/specificity/
http://coding.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/
http://reference.sitepoint.com/css/specificity
http://css-tricks.com/specifics-on-css-specificity/
The if never returns true because:
a) The selector text need not be exactly .yourClass. It can be .a .yourClass, div.yourClass or even .anotherClass, .yourClass etc.. all pointing to your element
b) The check needs to be something like:
for each selectorText {
strip off any { character
split the selector text by ',' into an array called parts
for each part {
check if it ends with '.yourClass' // if it does, this class is match
}
}
Another problem is CSS specificity. For counting in only those styles that will be applied at the end, you might need to do a lot more!
Related
I want to strip all elements within the DOM that contains useless spaces or are empty (I'm working with an outdated back-end system that generates some messy HTML and these elements can sometimes add unwanted spaces in the design). Using the filter function I'm able to test for specific cases but for some reason it doesn't seem to work with empty elements so I tried to test to see if any elements length is < 0 and then remove it. Why doesn't this work, and is there a way to do it with the filter function? I've tried
("<br>", "", " ");
but it doesn't seem to work.
stripEmpties();
function stripEmpties() {
var domChildren = $("*").children();
if (domChildren.length <= 0) {
$(this).remove();
} else {
domChildren.filter(function () {
return $.trim(this.innerHTML) === ("<br>", " ");
}).remove();
}
}
Actually after playing with it I now see that only the last match is even used in the filter so it looks like I can't use this list format I've come up with. So I guess another question would be is there a way to get similar functionality while checking multiple cases with the filter function?
It's not as easy as it sounds and it certainly is not efficient if the document is large. For example this will remove all br, img (tags with no innerHtml) etc tags aswell:
$(function () {
$("*").filter(function () {
return ($(this).text().trim() === "");
}).remove();
});
So maybe you should use a better selector than "*", for example "div".
How can I select nodes that begin with a "x-" tag name, here is an hierarchy DOM tree example:
<div>
<x-tab>
<div></div>
<div>
<x-map></x-map>
</div>
</x-tab>
</div>
<x-footer></x-footer>
jQuery does not allow me to query $('x-*'), is there any way that I could achieve this?
The below is just working fine. Though I am not sure about performance as I am using regex.
$('body *').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
Working fiddle
PS: In above sample, I am considering body tag as parent element.
UPDATE :
After checking Mohamed Meligy's post, It seems regex is faster than string manipulation in this condition. and It could become more faster (or same) if we use find. Something like this:
$('body').find('*').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
UPDATE 2:
If you want to search in document then you can do the below which is fastest:
$(Array.prototype.slice.call(document.all)).filter(function () {
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
There is no native way to do this, it has worst performance, so, just do it yourself.
Example:
var results = $("div").find("*").filter(function(){
return /^x\-/i.test(this.nodeName);
});
Full example:
http://jsfiddle.net/6b8YY/3/
Notes: (Updated, see comments)
If you are wondering why I use this way for checking tag name, see:
JavaScript: case-insensitive search
and see comments as well.
Also, if you are wondering about the find method instead of adding to selector, since selectors are matched from right not from left, it may be better to separate the selector. I could also do this:
$("*", $("div")). Preferably though instead of just div add an ID or something to it so that parent match is quick.
In the comments you'll find a proof that it's not faster. This applies to very simple documents though I believe, where the cost of creating a jQuery object is higher than the cost of searching all DOM elements. In realistic page sizes though this will not be the case.
Update:
I also really like Teifi's answer. You can do it in one place and then reuse it everywhere. For example, let me mix my way with his:
// In some shared libraries location:
$.extend($.expr[':'], {
x : function(e) {
return /^x\-/i.test(this.nodeName);
}
});
// Then you can use it like:
$(function(){
// One way
var results = $("div").find(":x");
// But even nicer, you can mix with other selectors
// Say you want to get <a> tags directly inside x-* tags inside <section>
var anchors = $("section :x > a");
// Another example to show the power, say using a class name with it:
var highlightedResults = $(":x.highlight");
// Note I made the CSS class right most to be matched first for speed
});
It's the same performance hit, but more convenient API.
It might not be efficient, but consider it as a last option if you do not get any answer.
Try adding a custom attribute to these tags. What i mean is when you add a tag for eg. <x-tag>, add a custom attribute with it and assign it the same value as the tag, so the html looks like <x-tag CustAttr="x-tag">.
Now to get tags starting with x-, you can use the following jQuery code:
$("[CustAttr^=x-]")
and you will get all the tags that start with x-
custom jquery selector
jQuery(function($) {
$.extend($.expr[':'], {
X : function(e) {
return /^x-/i.test(e.tagName);
}
});
});
than, use $(":X") or $("*:X") to select your nodes.
Although this does not answer the question directly it could provide a solution, by "defining" the tags in the selector you can get all of that type?
$('x-tab, x-map, x-footer')
Workaround: if you want this thing more than once, it might be a lot more efficient to add a class based on the tag - which you only do once at the beginning, and then you filter for the tag the trivial way.
What I mean is,
function addTagMarks() {
// call when the document is ready, or when you have new tags
var prefix = "tag--"; // choose a prefix that avoids collision
var newbies = $("*").not("[class^='"+prefix+"']"); // skip what's done already
newbies.each(function() {
var tagName = $(this).prop("tagName").toLowerCase();
$(this).addClass(prefix + tagName);
});
}
After this, you can do a $("[class^='tag--x-']") or the same thing with querySelectorAll and it will be reasonably fast.
See if this works!
function getXNodes() {
var regex = /x-/, i = 0, totalnodes = [];
while (i !== document.all.length) {
if (regex.test(document.all[i].nodeName)) {
totalnodes.push(document.all[i]);
}
i++;
}
return totalnodes;
}
Demo Fiddle
var i=0;
for(i=0; i< document.all.length; i++){
if(document.all[i].nodeName.toLowerCase().indexOf('x-') !== -1){
$(document.all[i].nodeName.toLowerCase()).addClass('test');
}
}
Try this
var test = $('[x-]');
if(test)
alert('eureka!');
Basically jQuery selector works like CSS selector.
Read jQuery selector API here.
Is it possible to remove all attributes at once using jQuery?
<img src="example.jpg" width="100" height="100">
to
<img>
I tried $('img').removeAttr('*'); with no luck. Anyone?
A simple method that doesn't require JQuery:
while(elem.attributes.length > 0)
elem.removeAttribute(elem.attributes[0].name);
Update: the previous method works in IE8 but not in IE8 compatibility mode and previous versions of IE. So here is a version that does and uses jQuery to remove the attributes as it does a better job of it:
$("img").each(function() {
// first copy the attributes to remove
// if we don't do this it causes problems
// iterating over the array we're removing
// elements from
var attributes = $.map(this.attributes, function(item) {
return item.name;
});
// now use jQuery to remove the attributes
var img = $(this);
$.each(attributes, function(i, item) {
img.removeAttr(item);
});
});
Of course you could make a plug-in out of it:
jQuery.fn.removeAttributes = function() {
return this.each(function() {
var attributes = $.map(this.attributes, function(item) {
return item.name;
});
var img = $(this);
$.each(attributes, function(i, item) {
img.removeAttr(item);
});
});
}
and then do:
$("img").removeAttributes();
One-liner, no jQuery needed:
[...elem.attributes].forEach(attr => elem.removeAttribute(attr.name));
Instead of creating a new jQuery.fn.removeAttributes (demonstrated in the accepted answer) you can extend jQuery's existing .removeAttr() method making it accept zero parameters to remove all attributes from each element in a set:
var removeAttr = jQuery.fn.removeAttr;
jQuery.fn.removeAttr = function() {
if (!arguments.length) {
this.each(function() {
// Looping attributes array in reverse direction
// to avoid skipping items due to the changing length
// when removing them on every iteration.
for (var i = this.attributes.length -1; i >= 0 ; i--) {
jQuery(this).removeAttr(this.attributes[i].name);
}
});
return this;
}
return removeAttr.apply(this, arguments);
};
Now you can call .removeAttr() without parameters to remove all attributes from the element:
$('img').removeAttr();
One very good reason to do this for specific tags is to clean up legacy content and also enforce standards.
Let's say, for example, you wanted to remove legacy attributes, or limit damage caused by FONT tag attributes by stripping them.
I've tried several methods to achieve this and none, including the example above, work as desired.
Example 1: Replace all FONT tags with the contained textual content.
This would be the perfect solution but as of v1.6.2 has ceased to function. :(
$('#content font').each(function(i) {
$(this).replaceWith($(this).text());
});
Example 2: Strip all attributes from a named tag - e.g. FONT.
Again, this fails to function but am sure it used to work once upon a previous jQuery version.
$("font").each(function() {
// First copy the attributes to remove.
var attributes = $.map(this.attributes, function(item) {
return item.name;
});
// Now remove the attributes
var font = $(this);
$.each(attributes, function(i, item) {
$("font").removeAttr(item);
});
});
Looking forward to 1.7 which promises to include a method to remove multiple attributes by name.
One-liner.
For jQuery users
$('img').removeAttr(Object.values($('img').get(0).attributes).map(attr => attr.name).join(' '));
One don't need to refer to the name of attribute to to id nowadays, since we have
removeAttributeNode method.
while(elem.attributes.length > 0) {
elem.removeAttributeNode(elem.attributes[0]);
}
I don't know exactly what you're using it for, but have you considered using css classes instead and toggling those ? It'll be less coding on your side and less work for the browser to do. This will probably not work [easily] if you're generating some of the attributes on the fly like with and height.
This will remove all attributes and it will work for every type of element.
var x = document.createElement($("#some_id").prop("tagName"));
$(x).insertAfter($("#some_id"));
$("#some_id").remove();
Today I have same issue. I think that it will be useful for you
var clone = $(node).html();
clone = $('<tr>'+ clone +'</tr>');
clone.addClass('tmcRow');
What jquery can I use to change the class that's used in the following from indent_1 to indent_2 or indent_4 to indent_2? I know about remove class but how can I do that when the classes are names that vary?
<div class="rep_td0 indent_1" id="title_1">Menu two</div>
or
<div class="rep_td0 indent_4" id="title_1">Menu two</div>
Since you haven't been very specific about exactly what class you want to change to another and you've said you want to deal with the case where you don't know exactly what the class is, here are some ideas:
You can find all objects that have a class that starts with "indent_" with this selector:
$('[className^="indent_"]')
If you wanted to examine the class on each one of those objects, you could iterate over that jQuery object with .each() and decide what to do with each object or you could use removeClass() with a custom function and examine the class name and decide what to do with it.
If you just wanted to change all indent class names to indent_2, then you could use this:
$('[className^="indent_"]').removeClass("indent_1 indent_3 indent_4").addClass("indent_2");
or, using a custom function that can examine the class name with a regex:
$('[className^="indent_"]').removeClass(function(index, name) {
var match = name.match(/\bindent_\d+\b/);
if (match) {
return(match[0]);
} else {
return("");
}
}).addClass("indent_2");
Or, if all you want to do is find the object with id="title_1" and fix it's classname, you can do so like this:
var o = document.getElementById("title_1");
o.className = o.className.replace(/\bindent_\d+\b/, "indent_2");
You can see this last one work here: http://jsfiddle.net/jfriend00/tF8Lw/
If you're trying to make this into a function that could take different numbers, you could use this:
function changeIndentClass(id, indentNum) {
var item = document.getElementById(id);
item.className = item.className(/\bindent_\d+\b/, "indent_" + indentNum);
}
try this code,
$("#title_1").removeClass("indent_1").addClass("indent_2");
if you not sure which is available, try this
$("#title_1").removeClass("indent_1").removeClass("indent_4").addClass("indent_2");
Updated:
$("#title_1").removeClass(function() {
var match = $(this).attr('class').match(/\bindent_\d+\b/);
if (match) {
return (match[0]);
} else {
return ("");
}
}).addClass("indent_2");
Try below :
$('#title_1').removeClass('indent_1').addClass('indent_2');
Here, the indent_1 classes will be replaced with indent_2.
Maybe this?: Remove all classes that begin with a certain string
That answers how to replace a classname on a jQuery element that has a specific prefix, such as 'indent_'.
While that answer doesn't specifically address replacement, you can achieve that by altering one of their answers slightly:
$("selector").className = $("selector").className.replace(/\bindent.*?\b/g, 'indent_2');
Or similar...
First things first...If you have multiple elements on your page with the exact same ID you're going to have problems selecting them by ID. Some browsers won't work at all while others may return just the first element that matches.
So you'll have to clean up the ID thing first.
You can use the starts with selector to find all the classes that match your class name patter and then decide if you want to switch them or not:
$('[class^="indent_"]').each(function() {
var me = $(this);
if(me.hasClass("indent_1").removeClass("indent_1").addClass("indent_2");
});
I'm trying to use jQuery to replace all occurrences of a particular string that occurs in a certain class. There are multiple classes of this type on the page.
So far I have the following code:
var el = $('div.myclass');
if(el != null && el.html() != null )
{
el.html(el.html().replace(/this/ig, "that"));
}
This doesn't work if there is more than one div with class myclass. If there is more than one div then the second div is replaced with the contents of the first! It is as if jQuery performs the replacement on the first div and then replaces all classes of myclass with the result.
Anyone know how I should be doing this? I'm thinking some kind of loop over all instances of mychass divs - but my JS is a bit weak.
I think what you are looking for is something like this:
$('div.myclass').each(function(){
var content = $(this).html();
content = content.replace(/this/ig,'that');
$(this).html(content);
});
(not tested)
slightly different of previous answer:
$('div.myclass').each(function(i, el) {
if($(el).html() != "" ) {
$(el).html($(el).html().replace(/this/ig, "that"));
}
});
should work
If the contents of your .myclass elements are purely textual, you can get away with this. But if they contain other elements your regex processing might change attribute values by mistake. Don't process HTML with regex.
Also by writing to the innerHTML/html(), you would lose any non-serialisable data in any child element, such as form field values, event handlers and other JS references.
function isTextNode(){
return this.nodeType===3; // Node.TEXT_NODE
}
$('div.myclass, div.myclass *').each(function () {
$(this).contents().filter(isTextNode).each(function() {
this.data= this.data.replace(/this/g, 'that');
});
});