Determine if a string is a valid jQuery selector? - javascript

Does jQuery have a method to determine if an argument passed to function is a selector?
I am making a template for some jQuery plugins and I need to be able to check if the argument passed in is a jQuery selector. I want to allow for other data types and perform different methods based on what data type is passed. Detecting data types is easy, but selectors are just a string and can be constructed is many different ways.
My goal is to create plugins that are forgiving with what you pass in for the arguments and makes educated decisions about what to do with it. Take the jQuery UI plugins for example, in some plugins, lets say we pass a callback function in the argument place holder that is for a number for a speed, it still takes the callback and runs it and uses the default for speed. That's the kind of functionality I'm going for and selectors are a pretty unique case.
Has jQuery written a Regex for this? I couldn't find one in the code.
If not, I guess I'll just have to write a huge Regex for this?

To go straight to the point:
No, jQuery has no method to check if a selector is valid.
jQuery has lots of Regex' for this, that's why you cannot find one in the code.
You do not have to write a huge Regex for this, the answer is more simple as shown below.
I understand your problem because I experienced it, there are cases in which you're not in control of the selector to give to the jQuery function.
The problem not outlined enough is that if a selector is not valid jQuery throws an error (it is important because here is the answer).
Example using jQuery v1.9.1:
$('##somewhere');
It logs into the console the following line:
throw new Error( "Syntax error, unrecognized expression: " + msg );
which source is at row 4421 of the jQuery.js file (non-minified and uncompressed version).
So, instead of looking for an inner method of jQuery (which could surely simplify things but is not available), you can just listen the error thrown to establish if the selector is valid:
function isValidSelector(selector) {
if (typeof(selector) !== 'string') {
return false;
}
try {
var $element = $(selector);
} catch(error) {
return false;
}
return true;
}
You can also make it a jQuery plugin:
jQuery.extend({
isValidSelector: function(selector) {
if (typeof(selector) !== 'string') {
return false;
}
try {
var $element = $(selector);
} catch(error) {
return false;
}
return true;
}
});
to be used this way:
alert($.isValidSelector('#what?!?'));
Best regards.
EDIT:
added type validation: the selector must be a string.
Anyhow it is a partial solution, it does not return false for selectors defined as string objects var selector = new String('##wrong-selector#!');, which are not a primitive type.

Lots of strings can technically be a selector like $('blah') could select custom elements! There isn't any good way of knowing the intent of what to do with the argument passed to your function, so it's best to have a well defined structure like Gaby has commented.
Selector:
yourFunction({ selector: 'div' });
Or
yourFunction({ value: 'testing' });
Will take a different route in your code.
Without this technique the best you can do is just attempt for jQuery to find elements based on the selector, check with .length, if elements are found then assume the caller intended a jQuery selector. Another option could be just to document that a jQuery object must be passed i.e.:
yourFunction({ value: jQuery('div') });
Then for a different route you can do
if (value instanceof of jQuery) { .... }

There can not be a regex for this, since the selectors are extensible and anyone could add any number of personal selectors (with custom defined symbols etc) ...
Perhaps you should try passing your arguments as a single object with named parameters.
{ selector:'...',
otherargument:'somevalue',
afunction: function(){...}
}

The jQuery code for determining the selector is valid is about 108 lines long, so don't expect to determine if it's a valid selector or not in one RegEx statement.
Your best bet is probably to look at what jQuery determines to be a valid selector, and make a function that essentially checks the same way, but returns whether it's valid or not.
https://github.com/jquery/jquery/blob/master/src/core.js#L80-188

This does not answer your question, but i think it can be helpful. It checks not if an argument is a jQuery selector, however it tests whether the selector exists in the document.
$.fn.inDom = function() { return $(document).find(this).length; };
BTW: I dont use $(selector).length directly, since it will return 1 if the passed argument is an HTMLNode.
For better interpretation purposes:
$('foo').length // 0
$('.foo').length // 0
$('#foo').length // 0
$('<foo>').length // 1
$(document).find('foo').length // 0
$(document).find('.foo').length // 0
$(document).find('#foo').length // 0
$(document).find('<foo>').length // 0

Related

"Primitive value will be lost" warning in WebStorm [duplicate]

I created a function:
function CheckHidden(el){ return $(el).css('display')!='none' }
My IDE warns me that:
Primitive value returned from constructor will be lost when called with 'new'
Actually when I call it like this:
var all = $("#catalog-body > div").filter(function(){return CheckHidden(this)})
it doesn't work and doesn't reduce set of elements to unhidden ones.
Please explain to me the issue. I have a giant lack of knowledge.
I ran into this warning myself and if you want to know the cause, it is because your IDE expect function names to start with a lowercase letter. Since your function is called CheckHidden with a capital C, it thinks it's a class declaration.
However, you should still use jQuery's :visible selector to fix your specific issue.
If you create an object using the keyword new (as mentioned in the warning), JS returns an new instance of the object rather than the return value from the constructor.
A function like
function CheckHidden() {
return false;
}
would return two different values, depending on how it is executed.
// "a" is boolean value
var a = CheckHidden();
// "a" is an object of type "CheckHidden"
var a = new CheckHidden();
jQuery actually has something like this built in. You can use the :visible selector. You can just do:
var all=$("#catalog-body > div").filter(':visible')
to get the visible elements.
If you want to pull all elements which are visible, you can use jQuery's :visible selector:
var all = $("#catalog-body > div:visible")
Conversely, if you want to pull all elements which are hidden, you can combine this with jQuery's :not selector:
$("#catalog-body > div:not(:visible)")

Array check element ID with wildcard in if statement

I can check an object ID in a array with
if (obj[0].id != "myID")
I would like to do the same with a wildcard, so that
if (obj[0].id != "myID*")
will exclude #myID1, #myID2, #myID3 etc.
I have to stay inside the if statement for this check, I can't call an external function.
If it is not possible, I can use obj[0].className instead of .id :
if (obj[0].className != "myClass")
but every object has several classes in addition of myClass.
jQuery is allowed although I'm not sure it will help.
If you're using jQuery (you've added the tag), why not use the selectors?
$('*:not[id^="myID"]')
This gets all the elements where the attribute does not start with myID. You can use this in your if statement like so:
if($(obj[0]).is('[id^="myID"]'))
First of all, you can definitely use an id attribute selector like this
if(!$(obj[0]).is("[id^=myID]"))
However, why not assign a class to all those elements instead? That sounds like a much more reasonable approach, allowing
if(!$(obj[0]).hasClass("myClass"))
Using String.prototype.indexOf might be one possible approach:
if (obj[0].id.indexOf('myID') !== 0) {
// ID does not start with 'myID'
}
You can even use regular expressions:
if( !/(myId)/g.test( obj[0].id.indexOf('myID') ) ) {
}
I can suggest you this really good playground to test you regexp:
http://lea.verou.me/regexplained/
And this talk:
http://www.youtube.com/watch?v=EkluES9Rvak
Regular expression can be very powerful. Maybe your case is not that hard to be managed with other tecniques but you would find regular expressions reeeally useful in the future for other problems.
You could check that the first 4 characters are myID with .substring():
if(obj[0].id.substring(0,4) != 'myId'){ }
If you wanted to use jQuery it would be really easy to check the id or class:
if(!$(obj[0]).is('[id^=myId]')){ }
or
if(!$(obj[0]).hasClass('myClass')){ }

Getting the "match" object in a Custom Filter Selector in jQuery 1.8

For reference, here's an article on Creating a Custom Filter Selector with jQuery.
Introduction:
For those not familiar with jQuery's Custom Filter Selectors, here's a quick primer on what they are:
If you need a reusable filter, you can extend jQuery’s selector expressions by adding your own functions to the jQuery.expr[':'] object.
The function will be run on each element in the current collection and should return true or false (much like filter). Three bits of information are passed to this function:
The element in question
The index of this element among the entire collection
A match array returned from a regular expression match that contains important information for the more complex expressions.
Once you've extended jQuery.expr[':'], you can use it as a filter in your jQuery selector, much like you would use any of the built-in ones (:first, :last, :eq() etc.)
Here's an example where we'll filter for elements that have more than one class assigned to them:
jQuery.expr[':'].hasMultipleClasses = function(elem, index, match) {
return elem.className.split(' ').length > 1;
};
$('div:hasMultipleClasses');
Here's the fiddle: http://jsfiddle.net/acTeJ/
In the example above, we have not used the match array being passed in to our function. Let's try a more complex example. Here we'll create a filter to match elements that have a higher tabindex than the number specified:
jQuery.expr[':'].tabindexAbove = function(elem, index, match) {
return +elem.getAttribute('tabindex') > match[3];
};
$('input:tabindexAbove(4)');
Here's the fiddle: http://jsfiddle.net/YCsCm/
The reason this works is because the match array is the actual array returned from the regex that was used to parse the selector. So in our example, match would be the following array:
[":tabIndexAbove(4)", "tabIndexAbove", "", "4"]
As you can see, we can get to the value inside the parentheses by using match[3].
The Question:
In jQuery 1.8, the match array is no longer being passed in to the filter function. Since we have no access to the info being passed in, the tabindexAbove filter does not work anymore (the only difference between this fiddle and the one above, is that this uses a later version of jQuery).
So, here are several points I'd like clarified:
Is this expected behavior? Is it documented anywhere?
Does this have anything to do with the fact that Sizzle has been updated (even though it clearly states that "the old API for Sizzle was not changed in this rewrite". Maybe this is what they mean by "the removal of the now unnecessary Sizzle.filter")?
Now that we have no access to the match array, is there any other way to get to the info being passed in to the filter (in our case, 4)?
I never found any documentation in the jQuery Docs about the custom filter selectors, so I don't know where to start looking for information about this.
jQuery has added a utility for creating custom pseudos in Sizzle. It's a little more verbose, but it's much more readable than using match[3]. It also has the advantage of being more performant as you can avoid repeating tedious calculations every time an element is tested. The answer that has already been accepted is a good answer, but let me add a note to say that you can use $.expr.createPseudo instead of setting the sizzleFilter property yourself, which will save a little space.
jQuery.expr[':'].tabIndexAbove = $.expr.createPseudo(function( tabindex ) {
return function(elem) {
return +elem.getAttribute('tabindex') > tabindex;
}
});
$('input:tabIndexAbove(4)').css('background', 'teal');
jsfiddle: http://jsfiddle.net/timmywil/YCsCm/7/
This is all documented on Sizzle's github:
https://github.com/jquery/sizzle/wiki/Sizzle-Documentation
By looking at the jQuery 1.8 beta2 source and the "Extensibility" section of The New Sizzle, you have to set fn.sizzleFilter to true in order to get the pseudo argument and the context. If not, you'll just get all the elements in the arguments.
Here is the code that does the same thing as your example. Use the selector parameter passed in the function to get the pseudo argument.
Here is the working example on jsfiddle.
As mentioned in the blog post above, you can even pre-compile and cache the your selector.
var sizzle = jQuery.find;
var tabIndexAbove = function( selector, context, isXml ) {
return function( elem ) {
return elem.getAttribute("tabindex") > selector;
};
};
/*
fn.sizzleFilter is set to true to indicate that tabIndexAbove
is a function that will return a function for use by the compiler
and should be passed the pseudo argument, the context, and
whether or not the current context is xml. If this property is
not set, adding pseudos works similar to past versions of Sizzle
*/
tabIndexAbove.sizzleFilter = true;
sizzle.selectors.pseudos.tabIndexAbove = tabIndexAbove;
$('input:tabIndexAbove(4)').css('background', 'teal');
Just a note, if you're looking at the source, jQuery slightly changed the structure that the public-facing interface points to.
In jQuery 1.7.2:
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
In jQuery 1.8b2:
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;

What does the jQuery function $('#myelement').is('*') do?

What does the following code do:
$('#myelement').is('*')
What does the asterisk signify? Since there is only one element, #myelement, I can't understand the point of using is(), which checks if an element matches a set of elements?
This is some seriously existential JavaScript.
$('#myelement').is('*')
It will fail whenever #myelement doesn't exist, and return true otherwise.
Basically check to see if an element exists or not. Not the best method...
is checks the element fits the criteria. In this case, "*" means all elements.
So, it simply returns true if the previous selector returns anything.
Take a look here for an example: http://jsfiddle.net/b7DwB/
http://api.jquery.com/is/
Pretty much what it does well from my understanding of it at least, and how I tend to use it. Is return true or false on whatever its called on.
Example I have a checkbox that I want to make sure is checked before I submit my form via AJAX I would do something like
if( $('input#tosCheck').is(':checked') ){
/*its checked submit form*/
}else{
alert('Error');
}
All in all the link to the API from jQuery better describes it then I ever could, but I wanted to at least share an example of use to help you gauge some idea.
Can't say I've ever seen that jQuery code used before, but it seems to be a poor way of checking for the existence of an element. Since * is the universal selector, the expression in question will always return true if #myelement exists, otherwise it will return false.
I say this is a "poor" way of checking the existence of an element because you can simply check the length of the jQuery object instead:
$('#myelement').length > 0
I haven't done any testing, but I assume the above is faster since it doesn't have the overhead of the is() function call.

jQuery 1.3 - Issue with Setting an Attribute Value

This is my first stackoverflow question, so try to be nice. ;-D
My issue is this, I am refactoring some existing javascript code and using jQuery to do it. In several places I've come across javascript code similar to the following:
// some js code working with the customAttribute value
javascriptElementObject.customAttribue = void(0);
The javascriptElementObject is now a jQuery object and I have been attempting to use the following code to do the same thing:
// some js code working with the customAttribute value
javascriptElementObject.attr("customAttribute", void(0));
However, this does not seem to be doing anything. The following code works however:
javascriptElementObject.get(0).customAttribute = void(0);
I'm aware of jQuery's removeAttr() function, but have not used it so far because I don't know if it's equivalent to setting the attribute value to void(0).
So I guess that really means I have 2 questions:
Why doesn't the first jQuery version work?
Are .get(0).customAttribue = void(0); and .removeAttr("customAttribute); equivalent?
Thanks.
jQuery likes to overload its methods so:
obj.attr( name ) //retrieves the attribute value
obj.attr( name, value ) //sets the attribute
obj.attr( name, void(0) ) == obj.attr( name, null ) == obj.attr( name ) //i.e retrieving the attribute
You might want to try the following if you want to set an empty attribute
obj.attr( name, '' )
This will also apply to other methods jQuery.html() for example
What are you trying to accomplish?
If the goal is to remove the value in the name/value pair, you might as well just remove the attribute entirely. I'm not aware of any intrinsic value in maintaining an attribute that has no value; in less standards-compliant browsers it may even cause a problem.
In general, the syntax of $(selector).attr(name, value) and $(selector).removeAttr(name) work very well (at least I've never seen it fail.)
If you're trying to use void(0) to keep A HREFs from firing you'd be better off using a "return false" as the click event on those A tags.
The only way to work with custom attributes via jQuery objects is:
obj.get(0).myCustomAttr = 'some value';
That is because jQuery's attr() method will not work with custom attributes (except while applied on a XML-document).
Note also that meouw's answer regarding jQuery overloading functions is not precisely correct, because jQuery checks for the parameters passed to it in such a manner that:
jQuery.funcname(param)
and
jQuery.funcname(param, null)
differ, becacuse null !== undefined. For example:
var params_test = function(a) {
if (a === undefined) {
return 'called with no parameters';
} else {
return 'called with one parameter: ' + a;
}
};
params_test(); // results in 'called with no parameters'
params_test(null); // results in 'called with one parameter: null'
Uhmm, try this:
javascriptElementObject.attr("customAttribute", void(0));
var _void = javascriptElementObject.attr("customAttribute");
alert(_void);

Categories

Resources