Find HTML based on partial attribute - javascript

Is there a way with javascript (particularly jQuery) to find an element based on a partial attribute name?
I am not looking for any of the selectors that find partial attribute values as referenced in these links:
starts with [name^="value"]
contains prefix [name|="value"]
contains [name*="value"]
contains word [name~="value"]
ends with [name$="value"]
equals [name="value"]
not equal [name!="value"]
starts with [name^="value"]
but more something along the lines of
<div data-api-src="some value"></div>
<div data-api-myattr="foobar"></div>
and
$("[^data-api]").doSomething()
to find any element that has an attribute that starts with "data-api".

This uses .filter() to limit the candidates to those that has data-api-* attributes. Probably not the most efficient approach, but usable if you can first narrow down the search with a relevant selector.
$("div").filter(function() {
var attrs = this.attributes;
for (var i = 0; i < attrs.length; i++) {
if (attrs[i].nodeName.indexOf('data-api-') === 0) return true;
};
return false;
}).css('color', 'red');
Demo: http://jsfiddle.net/r3yPZ/2/
This can also be written as a selector. Here's my novice attempt:
$.expr[':'].hasAttrWithPrefix = function(obj, idx, meta, stack) {
for (var i = 0; i < obj.attributes.length; i++) {
if (obj.attributes[i].nodeName.indexOf(meta[3]) === 0) return true;
};
return false;
};
Usage:
$('div:hasAttrWithPrefix(data-api-)').css('color', 'red');
Demo: http://jsfiddle.net/SuSpe/3/
This selector should work for pre-1.8 versions of jQuery. For 1.8 and beyond, some changes may be required. Here's an attempt at a 1.8-compliant version:
$.expr[':'].hasAttrWithPrefix = $.expr.createPseudo(function(prefix) {
return function(obj) {
for (var i = 0; i < obj.attributes.length; i++) {
if (obj.attributes[i].nodeName.indexOf(prefix) === 0) return true;
};
return false;
};
});
Demo: http://jsfiddle.net/SuSpe/2/
For a more generic solution, here's a selector that takes a regex pattern and selects elements with attributes that match that pattern:
$.expr[':'].hasAttr = $.expr.createPseudo(function(regex) {
var re = new RegExp(regex);
return function(obj) {
var attrs = obj.attributes
for (var i = 0; i < attrs.length; i++) {
if (re.test(attrs[i].nodeName)) return true;
};
return false;
};
});
For your example, something like this should work:
$('div:hasAttr(^data-api-.+$)').css('color', 'red');
Demo: http://jsfiddle.net/Jg5qH/1/

Not sure what it is you're looking for, but just spent a few minutes writing this:
$.fn.filterData = function(set) {
var elems=$([]);
this.each(function(i,e) {
$.each( $(e).data(), function(j,f) {
if (j.substring(0, set.length) == set) {
elems = elems.add($(e));
}
});
});
return elems;
}
To be used like :
$('div').filterData('api').css('color', 'red');
And will match any elements with a data attribute like data-api-*, and you can extend and modify it to include more options etc. but of right now it only searches for data attributes, and only matches 'starts with', but at least it's simple to use ?
FIDDLE

Related

Javascript css selector for nested classes

I am creating a CSS selector for homework. I have managed to extract and get single selectors - e.g. #_id, but I cannot work out how to get a result for nested ones such as : div#_id._class [NOTE: I cannot use any libraries to do this or querySelectorAll]
The pseudo-code below is an example of what I currently have:
if (regex match for class) {
for (a in match for class) {
if (a.indexOf('.') > -1) {
var split_ = a.split(".");
var dot = split_[0];
var class_ = split_[1];
array_of_elements = document.getElementsByClassName(class_);
}
}
The problem is when the selector is nested I can't extract the whole thing using a similar method. E.g. look for an id, look for a class. Can anyone point me in the right direction?
else if (is id) {
split by ("#");
for (each result) {
if (has class ('.')) {
array_elements = document.getElementById(result_ID)
.getElementsByClassName(result_CLASS_NAME));
} else {
array_elements = (document.getElementsByTagName(result));
}
}
What you mentioned is actually called a sequence of simple selectors.
div#_id._class
It consitst of three simple selectors div, #_id, ._class
What you need to do is get elements by tag name, and then check for matches on all of the remaining simple selectors. I'll give you an idea here:
function qSelector(sequence) {
var tagName = getTag(sequence) || '*'; // 'div'
var ids = getIDs(sequence); // ['_id']
var classes = getClasses(sequence); // ['_class']
var els = document.getElementsByTagName(tagName);
return [].filter.call(els, function (el) {
for (id in ids) { if (el.id != id) return false; }
for (cls in classes) { if (el.className not contains cls) return false; }
return true;
});
}
This is more versatile than your approach and can be easily generalized to work with selectors containing spaces.
I'll leave the implementation of the get… helpers to you.

Jquery length of matched query

How would I write both of these without using .each() and only using JQuery Selectors?
var xxxx = 0;
$('.clonedInput').each(function(index) {
if($(this).children().filter(':checked').length == 2)
xxxx++;
});
var num_normal_foods = 0;
$('[id^="amount_"]').each(function(index) {
if($(this).val() == '30.00')
num_normal_foods++;
});
Lets take this one step at a time.
You started with:
var xxxx = 0;
$('.clonedInput').each(function(index) {
if($(this).children().filter(':checked').length == 2)
xxxx++;
});
To me this looks like you're simply trying to filter a collection of .clonedInput elements and find out how many match the filter:
var xxxx;
function hasTwoCheckedChildren(i) {
return $(this).children(':checked').length == 2;
}
xxxx = $('.clonedInput').filter(hasTwoCheckedChildren).length;
Followed by:
var num_normal_foods = 0;
$('[id^="amount_"]').each(function(index) {
if($(this).val() == '30.00')
num_normal_foods++;
});
Again, this looks like a filtering function to me:
var num_normal_foods;
function valueIsThirty(i) {
return +$(this).val() === 30;
}
num_normal_foods = $('[id^="amount_"]').filter(valueIsThirty).length;
In the end, what matters is that the code does what you intend it to do. If the code you wrote with .each does what you want it to, then there's no need to change it. Behind-the-scenes filter uses each anyway.
jQuery selections have a .length property:
var len = $('.clonedInput :checked').length;
var len2 = $('[id^="amount_"][value="30.00"]').length;
the first query returns all of the checked children of any .clonedInput class, then counts them.
the second query finds all of the id's that begin with amount_ and have a value of "30.00". (property queries can be chained like that [][])
EDIT: to satisfy #Blazemonger
to get the value of any type of element (value works on some), use this:
var len2 = $('[id^="amount_"]').filter(function() {
return $(this).val() == "30.00";
}).length;
Double EDIT because i'm useless
var len = $('.clonedInput').filter(function(){
return $(this).children(':checked').length == 2;
}).length;

How do I find if an element contains a specific class with jQuery?

I need to check if an element contains a certain child class using JQUERY.
I tried:
if ($('#myElement').has('.myClass')) {
do work son
}
Didn't work.
My html code is laid out like this:
<div id="myElement">
<img>
<span>something</span>
<span class="myClass">Hello</span>
</div>
The easiest way would be to search for .myClass as a child of #myElement:
if($('#myElement .myClass')).length > 0)
If you only want first level children, you'd use >
if($('#myElement > .myClass')).length > 0)
Another way would be passing a selector to find and checking for any results:
if($('#myElement').find('.myClass').length > 0)
Or for first level children only:
if($('#myElement').children('.myClass').length > 0)
Just use QS
var hasClass = document.getElementById("myElement").querySelector(".myClass");
or you could recurse over the children
var element = document.getElementById("myElement");
var hasClass = recursivelyWalk(element.childNodes, function hasClass(node) {
return node.classList.contains("myClass");
});
function recursivelyWalk(nodes, cb) {
for (var i = 0, len = nodes.length; i < len; i++) {
var node = nodes[i];
var ret = cb(node);
if (ret) {
return ret;
}
if (node.childNodes && node.childNodes.length) {
var ret = recursivelyWalk(node.childNodes, cb);
if (ret) {
return ret;
}
}
}
}
Using recursivelyWalk and .classList (which can be shimmed).
Alternatively you can use jQuery
$("#myElement .myClass").hasClass("myClass");
or if you want composite operations without jQuery then try NodeComposite
NodeComposite.$("#myElement *").classList.contains("myClass");
Try:
if($('#myElement').children('.myClass').length) {
// Do what you need to
}
The jQuery object returns an array, which has the .length property. The above code checks if there are any .myClass children in #myElement and, if there are (when .length isn't 0), executes the code inside the if() statement.
Here's a more explicit version:
if($('#myElement').children('.myClass').length > 0) {
// Do what you need to
}
You could always use $('#myElement .myClass').length too, but $.children() is clearer to some. To find elements that aren't direct children, use $.find() in place of $.children().
if($.contains($('#myElement'), $('.myClass'))){
alert("True");
}
else{alert("False")};

jQuery - has class that starts with

What would be the best way to get all divs that have any class that starts with input? In other words, a and b should be returned from what's below, but not c.
<div id="a" class="input1 foo"></div>
<div id="b" class="foo input2"></div>
<div id="c" class="xinput3 foo"></div>
The ostensible way, which surprisingly was accepted here, is to do $("div[class^='input']"); but of course that misses b. And of course $("div[class*='input']"); will give a false positive on c.
The best I could come up with was this monstrosity
function getAllInputDivs() {
return $("div").filter(function (i, currentDiv) {
return $.grep($(currentDiv).attr("class").split(" "), function (val) {
return val.substr(0, "input".length) === "input";
}).length > 0;
});
}
Is there a cleaner way? Here's a working fiddle of the above
You can create your own expression in jQuery
$.expr[':'].hasClassStartingWithInput = function(obj){
return (/\binput/).test(obj.className);
};
and you can retrieve those div with
$('div:hasClassStartingWithInput');
a JsFiddle Example: http://jsfiddle.net/7zFD6/
Edit: you could also use a parameter (without hardcoding the class name inside the function identifier) in this way
$.expr[':'].hasClassStartingWith = function(el, i, selector) {
var re = new RegExp("\\b" + selector[3]);
return re.test(el.className);
}
new example on http://jsfiddle.net/pMepk/1/
Here's one way...
function getAllInputDivs() {
return $("div").filter(function () {
return /(?:^|\s)input/.test(this.className);
});
}
Or make it more versatile...
function classStartsWith( tag, s ) {
var re = new RegExp('(?:^|\\s)' + s);
return $(tag || '*').filter(function () {
return re.test(this.className);
});
}
Or take the indexOf approach if you don't like regex...
function classStartsWith( tag, s ) {
return $(tag || '*').filter(function () {
return this.className.indexOf(s)===0 || this.className.indexOf(' ' + s)>-1;
});
}
Though you should be aware that it does't test for tab characters, only space characters, so it could fail if a tab was used instead of a space.
Going back to the regex versions, you can improve efficiency by adding the searched string to the selector.
Then it is only testing a subset of divs.
function getAllInputDivs() {
return $("div[class*='input']").filter(function () {
return /(?:^|\s)input/.test(this.className);
});
}
With the .filter() applied to only those divs that you know have input somewhere in the class, the performance will improve.
Or the versatile version would look like this:
function classStartsWith( tag, s ) {
var re = new RegExp('(?:^|\\s)' + s);
return $((tag || '*') + '[class*="' + s + '"]').filter(function () {
return re.test(this.className);
});
}
This is my solution for the problem:
(function($) {
$.fn.hasClassStartsWith = function(klass) {
var _return = [];
for(var i = 0; i < this.length; i++){
if((' ' + $(this[i]).attr('class')).indexOf(klass) != -1)
_return.push(this[i]);
}
return _return;
}
})(jQuery);
Use it as follows:
var divs = $('div').hasClassStartsWith("my_class_prefix");
It works also for the case someone creates a class with a dot in the middle.

Does JavaScript or jQuery have a function similar to Excel's VLOOKUP?

Do JavaScript or jQuery have a function that returns the element of an array whose index equal to the position of a given value in another array? (I could write my own, but I don't want to reinvent the wheel.)
Something like:
function vlookup(theElement, array1, array2) {
$.each(array1, function(index, element) {
if (element === theElement)
return array2[index];
});
return null;
}
But, um... in the standard library.
Something like this perhaps?
Array.prototype.vlookup = function(needle,index,exactmatch){
index = index || 0;
exactmatch = exactmatch || false;
for (var i = 0; i < this.length; i++){
var row = this[i];
if ((exactmatch && row[0]===needle) || row[0].toLowerCase().indexOf(needle.toLowerCase()) !== -1)
return (index < row.length ? row[index] : row);
}
return null;
}
Then you can use it against a double array, like so
Depending your purpose, you can modify the indexOf to make both strings lowercase first so the comparison doesn't fail with "foo" vs "FOO". Note also that if index exceeds the row's length, the entire row is returned (this can be changed to the first element (or whatever) easily by modifying the : row); portion.

Categories