I'm trying to search for all elements in a web page with a certain regex pattern.
I'm failing to understand how to utilize Javascript's regex object for this task. My plan was to collect all elements with a jQuery selector
$('div[id*="Prefix_"]');
Then further match the element ID in the collection with this
var pattern = /Prefix_/ + [0 - 9]+ + /_Suffix$/;
//Then somehow match it.
//If successful, modify the element in some way, then move onto next element.
An example ID would be "Prefix_25412_Suffix". Only the 5 digit number changes.
This looks terrible and probably doesn't work:
1) I'm not sure if I can store all of what jQuery's returned into a collection and then iterate through it. Is this possible?? If I could I could proceed with step two. But then...
2) What function would I be using for step 2? The regex examples all use String.match method. I don't believe something like element.id.match(); is valid?
Is there an elegant way to run through the elements identified with a specific regex and work with them?
Something in the vein of C#
foreach (element e in
ElementsCollectedFromIDRegexMatch) { //do stuff }
Just use the "filter" function:
$('div[id*=Prefix_]').filter(function() {
return /^Prefix_\d+_Suffix$/.test(this.id);
}).each(function() {
// whatever you need to do here
// "this" will refer to each element to be processed
});
Using what jQuery returns as a collection and iterating through it is, in fact, the fundamental point of the whole library, so yes you can do that.
edit — a comment makes me realize that the initial selector with the "id" test is probably not useful; you could just operate on all the <div> elements on the page to start with, and let your own filtering pluck out the ones you really want.
You can use filter function. i.e:
$('div[id*="Prefix_"]').filter(function(){
return this.id.match(/Prefix_\d+_Suffix/);
});
You could do something like
$('div[id*="Prefix_"]').each(function(){
if($(this).attr('id').search(/do your regex here/) != -1) {
//change the dom element here
}
});
You could try using the filter method, to do something like this...
var pattern = /Prefix_/ + [0 - 9]+ + /_Suffix$/;
$('div[id*="Prefix_"]').filter(function(index)
{
return $(this).attr("id").search(pattern) != -1;
}
);
... and return a jQuery collection that contains all (if any) of the elements which match your spec.
Can't be sure of the exact syntax, off the top of my head, but this should at least point you in the right direction
Related
Please, do not laugh, too much. I know jQuery ans JS for a short a while.
1) How can I make this code more efficient? First line is how do I "select" elements, the second, line is how do I prep to "select", next or previous element.
jQuery('code:lt('+((aktywneZdanie+1).toString())+'):gt('+((aktywneZdanie-1).toString())+')').removeClass('class2');}
aktywneZdanie=aktywneZdanie-1
2) I can not create a function which is working as a method. What I meant is how to change:
jQuery('#something').addClass('class1')
.removeClass('class2');
to something like this:
jQuery('#something').changeClasses();
function changeClasses(){
.addclass('class1');
.removeClass('class2');}
For the first one, why do you need a selector like that? couldn't you find something less specific to hook onto? If you must keep it when joining an number and a string, JavaScript will convert the number to string behind the scenes so you don't really need the .toString() and could do the "maths" +/-1 outside of your selector making it more readable.
Edit
In regards to your comment I am not really sure what you mean, you could assign a class to the "post" items and then add the unique id to a data-attribute ID. To make it simpler you could do something like this:
var codeLt = aktywneZdanie + 1,
codeGt = aktywneZdanie - 1;
$('code:lt(' + codeLt + '):gt(' + codeGt +')').removeClass('class2');
End Edit
And the second solution should work, all your doing is passing the dom elements found from your selector into a function as a jQuery "array" in which manipulate to your needs
And for your second question why not just toggle the class on and off? having a default state which reflects class one?
jQuery('#something').toggleClass('uberClass');
Or you can pass your selector to the function
changeClasses(jQuery('#something'));
Then inside you function work on the return elements.
Edit
Your code should work fine, but id suggest checking to make sure you have got and element to work on:
changeClasses(jQuery('#something'));
function changeClasses($element){
if($element.length > 0) {
$element.addClass('class1');
}
}
End Edit
Hope it helps,
1) How can I make this code more efficient? First line is how do I "select" elements, the second, line is how do I prep to "select", next or previous element.
jQuery('code:lt('+((aktywneZdanie+1).toString())+'):gt('+((aktywneZdanie-1).toString())+')').removeClass('class2');}
aktywneZdanie=aktywneZdanie-1
I stoped creating this wierd code like this one above, instead I start using .slice() (do not forget to use .index() for arguments here), .prev(), .next(). Just those three and everything is faster and clearer. Just an example of it below. No it does not do anything logical.
var activeElem = jQuery('code:first');
var old Elem;
jQuery('code').slice('0',activeElem.index()).addClass('class1');
oldElem=activeElem;
activeElem=activeElem.next();
jQuery('code').slice(oldElem.index(),activeElem.index()).addClass('class1');
oldElem.toggleClass('class1');
activeElem.prev().toggleClass('class1');
and the second part
2) I can not create a function which is working as a method. What I meant is how to change:
jQuery('#something').addClass('class1')
.removeClass('class2');
to something like this:
jQuery('#something').changeClasses();
function changeClasses(){
.addclass('class1');
.removeClass('class2');}
This one is still unsolved by me.
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.
What's the use of below snippet ? I extracted it from jQuery API. I don't understand it:
$("div").filter( $("#unique") )
Please be kind enough to explain this to me.
It is extracting the only one div with id=unique.
$('div'). // return all divs
filter( $('#unique') ); // take the div with id=unique
So. this statement will return you the div with id=unique.
Note
This statement can also be written as $('div#unique') or just $('#unique').
The filter method enables you to filter out only specific elements from amongst a selection. Say you want to choose all spans whose text contains more than 3 characters. So you would do this:
$("span").filter(function() { return $(this).text().length > 3; }).click(...);
The function should check for some condition and return a boolean. if it sends true that element is kept in the selection, else discarded. So for your current question, it would
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);
I want to do form validation at user side with JavaScript (jQuery is also used). The goal is to remove nested bbCode [quote] tags deeper than level 2. Say, we have this text:
[quote=SoundMAX][quote=Laplundik][quote=SoundMAX]
blahblahblah[/quote]
blahblah
[/quote]
blah[/quote]
And get this:
[quote=SoundMAX][quote=Laplundik]
blahblah
[/quote]
blah[/quote]
My only idea is to .replace [quote] with <div>, then create DOM object and remove anything deeper than 2 with jQuery, and parse all backwards to bbCode. But that solution seems too complicated, are there more elegant one?
EDIT:
Thanks for nice solutions. Based on darioo's answer, I did this:
var text=$('#edit-privatemsgbody').val();
var tmp=[];
var level=0;
for (var i=0,l=text.length;i<l;i++){
if(text[i]=='['&&text[i+1]=='q') level++;
if(text[i-6]=='q'&&text[i-7]=='/'&&text[i-8]=='[') level--;
if(level<3) tmp.push(text[i]);
}
alert(tmp.join(''));
Which works just fine.
But idealmachine's solution was like a flash. I didn't know about replace callback function parameters before, now that is handy! I'll settle with it.
Actually, you can use regex if you look at it as a limited tool that cannot handle the nesting itself. The .replace string method can call a function to find the replacement text for each match, which allows us to track how deep we are in the markup structure (code also posted at http://jsfiddle.net/Zbgr3/3/):
var quoteLevel = 0;
alert(s.replace(/\[(\/?)quote[^\]]*\]|./gi, function(tag, slash) {
// Opening tag?
if(tag.length > 1 && !slash.length) quoteLevel += 1;
// What to strip
var strip = quoteLevel > 2;
// Closing tag?
if(tag.length > 1 && slash.length) quoteLevel -= 1;
if(strip) return '';
return tag;
}));
If you want some tolerance for errors in the markup, you could add some extra code that, for example, prevents quoteLevel from falling below zero.
Use a regular array as a stack. Every time you encounter [quote], increase your array by one using its push() method. When you encounter [/quote], decrease your array by one using its pop() method.
If you encounter [quote] and your array length is 2, remove that [quote], and remove the next [/quote] you encounter.
If you don't have the same number of open and closed quotes, then you'll have to handle that in a way you find appropriate.