I am trying to modify a div that has a given class with the following jquery code:
$("[class^=delay-][class$='+number+']").each(function(index) {
var delayTime = $(this).attr('class').match(/delay-(\d+)/)[1];
$(this).removeClass("[class^=delay-][class$='+number+']");
$(this).data('data-wow-delay', delayTime + 's');
});
Find the divs that has delay-1, or delay-3, and so on...
Get the number as a variable.
Remove the class because I don't need it anymore.
Add to the div data-wow-delay="1s"
I am using the above script but it doesn't seem to succeed in identifying the class.
jquery wildcards don't work with removeClass
This is correct, because removeClass doesn't use a selector, it uses explicit class names - it's directly equivalent would be addClass, for which it makes no sense to have wildcards.
You can get all the classes and loop through them, giving an exact value for remove class, and, in your case for the delayTime value.
// match where all the classes might contain a relevant one
$("[class*='delay-']").each(function() {
// Keep 'this' for inside later loop
var el = $(this);
// get all the classes
var classList = el.attr('class').split(/\s+/);
// loop each class to check if it's one to remove
$.each(classList, function(index, item) {
if (item.startsWith("delay-")) {
el.removeClass(item);
var delayTime = parseInt(item.substring(6), 10);
el.data('data-wow-delay', delayTime + 's');
}
});
});
You could reduce the code with $.map, but this gives the idea.
Working fiddle: https://jsfiddle.net/hne72o8m/
The removeClass function takes a function argument since jQuery 1.4.
So it can be done with a one-liner. See Best answer in jQuery removeClass wildcard
Related
I have a variable that finds the data attribute of an element that is clicked on in a callback function:
var dropdown = document.getElementsByClassName('js-dropdown');
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", callBack (dropdown[i]));
}
function callBack (i) {
return function () {
var thisDropdown = i.getAttribute('data-dropdown');
//rest of the code here
}
}
I am basically trying to do this
$('#' + thisDropdown ).toggleClass('is-active');
...but in vanilla JS.
This works fine using jQuery however I would like a vanilla version.
So when a user clicks on an element that activates a drop down, I want it to dynamically find its relevant ID matching value within the document so it can toggle a show/hide class.
I've searched through a lot of SO questions and everyone replies with a jQuery answer which is not what I am looking for.
I've been trying to do something along the lines of
var idValue = document.getElementById(thisDropdown);
Then
var findId= idValue + thisDropdown;
findId.toggleClass('is-active');
Obviously that does not work the same way the jQuery statement works... any ideas?
Ignore the toggleClass method! Some of you may find this contradictory as I want vanilla JS.
To replace $('#' + thisDropdown ).toggleClass('is-active'); with plain js, use Element.classList. Like this:
const someElement = document.querySelector('#' + thisDropdown);
someElement.classList.toggle("is-active");
I like #kamyl's answer, but you might need backward compatibility. For that, see if you can find a polyfill.
If you have to write it yourself, use string.split(" ") to get your list of active attributes and iterate to find if it exists; add if not, remove if so...then array.join(" ") and replace the class attribute with it.
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');
I would like to animate to clear out a list of items at slightly different times, say in 20ms intervals. (Think of Android 4.2 alert screen when you clear messages).
However, I can't figure out how to addClass to different elements in an array at interval time. I've tried this, but this just applies the jQuery to the last element:
for item in App.itemsController.content
jQuery('#box').delay(2000).queue (n) ->
selector = '.' + item.objectId
console.log selector
$(selector).addClass('animated fadeOutRightBig')
I've also tried setTimeout() and setInterval() to no avail.
Thanks.
You need to use a closure to keep a reference to the item variable. I don't use CoffeeScript but in javascript I would do this.
var box = jQuery('#box');
for (var i in App.itemsController.content) {
var item = App.itemsController.content[i];
(function(item) {
box.delay(2000).queue(function() {
var selector = '.' + item.objectId;
$(selector).addClass('animated fadeOutRightBig');
});
}(item));
}
Maybe this is how you do it in CoffeeScript? Just had a quick look at the syntax.
for item in App.itemsController.content
do (item) ->
jQuery('#box').delay(2000).queue (n) ->
selector = '.' + item.objectId
console.log selector
$(selector).addClass('animated fadeOutRightBig')
You should also make a reference to the #box element outside the loop, otherwise your program has to search the DOM on every iteration.
Hi everyone,
Actually, i got "c.replace is not a function" while i was trying to delete some DOM elements and..i don't understand.
i'd like to delete some tags from the DOM and so, i did it :
var liste=document.getElementById("tabs").getElementsByTagName("li");
for(i=0;i<liste.length;i++)
{
if(liste[i].id==2)
{
$("#tabs").detach(liste[i]);
}
}
I tried .detach and .remove but it's the same. My version of jQuery is 1.7.1.min.js.
Thanks for help.
order of iteration on a NodeLIst
Doing forward iteration of a NodeList that is being modified when you remove an element can be an issue. Iterate in reverse when removing elements from the DOM.
misuse of detach()
Also, the arguments to .detach() do not perform a nested find, but rather act as a filter on the existing element(s) in the jQuery object, and should be passed a string. It seems that you actually want to detach the li, which would mean that you'd need to call .detach() on the li itself...
var liste=document.getElementById("tabs").getElementsByTagName("li");
var i = liste.length
while(i--) {
if(liste[i].id==2) {
$(liste[i]).detach();
}
}
remove() may be preferred
Keep in mind that if you use .detach(), any jQuery data is retained. If you have no further use for the element, you should be using .remove() instead.
// ...
$(liste[i]).remove(); // clean up all data
code reduction
Finally, since you're using jQuery, you could just do all this in the selector...
$('#tabs li[id=2]').remove(); // or .detach() if needed
valid id attributes
Keep these items in mind with respect to IDs...
It's invalid to have duplicate IDs on a page
It's invalid in HTML4 to have an ID that starts with a number
In the selector above, I used the attribute-equals filter, so it'll work, but you should really be using valid HTML to avoid problems elsewhere.
liste is not (yet) a jQuery object. use $(liste[i])
or use
var liste= $('#tabs li');
Maybe I'm missing something, but is the id suppose to match the number 2.
var liste=document.getElementById("tabs").getElementsByTagName("li");
for(i=0;i<liste.length;i++) {
if(liste[i].id==2) {
$(liste[i]).detach();
}
}
Since you are already using jQuery, why not just do:
$("li", "#tabs").filter("#2").detach();
var two = document.getElementById('2');
two.parentNode.removeChild(two);