I'm using the .length method in a conditional statement that polls the page for the presence of an externally-loaded object (I can't style it with jQuery until it exists):
function hackyFunction() {
if($('#someObject').length<1)
{ setTimeout(hackyFunction,50) }
else
{ $('#someObject').someMethod() }}
Is length the best way to do this?
If you are simply looking for a specific element you can just use document.getElementById
function hackyFunction() {
if (document.getElementById("someObject")) {
// Exist
} else {
// Doesn't exist
}
}
Yes you should use .length. You cannot use if ($('#someObject')) ... because the jQuery selectors return a jQuery object, and any object is truthy in JavaScript.
Yes, .length is acceptable and is usually what I use.
If you're looking for an ID there should only ever be one of those, so you could also write:
if($('#someObject')[0])
With jQuery, checking length works fine.
if (!$('#someObject').length) {
console.log('someObject not present');
}
Of course with vanilla JavaScript, you can just check with document.getElementById (if getting elements by id)
if (document.getElementById('someObject')) {
console.log('someObject exists');
}
Related
I've been looking all over the web for how to do this. I am trying to make Jquerys .html() function in vanilla JavaScript. I want to recreate so I can understand it better. I've tried the following but nothing seems to work, I just don't understand what I am doing wrong.
let $ = function(ele) {
if (ele.charAt(0) == '.' || ele.charAt(0) == '#') {
let newEle = cut(ele,0);
if (ele.charAt(0) == '#')
get(newEle);
else
return document.getElementsByClassName(newEle);
} else
return document.getElementById(ele);
}
$.prototype.html = function(html) {
this.innerHTML = html;
}
$('test').html('hey');
$('.hey')[0].html('hey');
function cut(string,num) {
let a = string.slice(0,num);
let b = string.slice(num + 1,string.length);
return a + b;
}
It doesn't work, the console log reports this error:
Uncaught TypeError: $(...).html is not a function
Please help and thanks in advance.
The problem here is what you are returning from the $ function.
Think about this: document.getElementsByClassName and document.getElementById return dom elements and dom elements don't have a .html function. That is why you are getting the error.
What you need is to return is an object, a wrapper, with a .html function, and a closure over the dom elements that you want to modify.
Because you're returning an object from $, you're overriding the default behavior of new $; instead of resulting in the newly-created object, it results in the object you returned out of $.
Instead, you'd want to remember the results of those getElementsByClassName/getElementById calls in an array or Set you store on this (the newly-created object), and then use that array or Set within html (since you want to loop over all matching elements in the case where you're setting the new HTML).
Side note: Since you're using ES2015+ features anyway (let), you might want to use the simpler class syntax instead of a separate function declaration and assigning to $.prototype:
class $ {
constructor(ele) {
// ...
}
html(html) {
// ..
}
}
I've gathered an Array (I think) of required form elements, and have added 'blur' listener.
var formInputs = $(':input').filter('[required]');
formInputs.each(function(i) {
$(this).on('blur', function() { // Each time we leave a 'required' field, check to see if we can activate the 'submit' button.
submitEnabler(formInputs);
});
});
So, once someone has left one of these fields, I want to run through this array using .every() and check if the fields are valid - that is if they have a 'success' class that I have defined.
function isValid(input) {
return input.hasClass('is_glowing_success');
}
function submitEnabler(inputs) {
console.log(inputs.every(isValid));
}
I keep getting back:
Uncaught TypeError: inputs.every is not a function
at submitEnabler
Now, I could do something like this...
for (var i = 0; i < inputs.length; i++) {
if ($(inputs[i]).hasClass('is_glowing_success')) {
console.log('yes');
} else {
console.log('no');
}
}
But, why can't I just use: Array.Prototype.every() ?
Because jQuery objects have no every method, and formInputs is a jQuery object.
If you want an array instead, call get() to get one.
I've gathered an Array (I think) of required form elements...
No, it's just jQuery object. jQuery objects are very array-like, but they aren't arrays. Worse, they have some array-like methods (such as filter and map) that call their callbacks with different arguments than the equivalent Array.prototype methods.
In isValid, you'd need to handle the fact you're now dealing with a raw DOM element, which means either wrapping it with a jQuery object and using hasClass:
function isValid(input) {
return $(input).hasClass('is_glowing_success');
}
or using the DOM's classList:
function isValid(input) {
return input.classList.contains('is_glowing_success');
}
That latter works on all modern browsers, but not all older ones. However, it can be polyfilled on older browsers. More about that on MDN.
jQuery does not have a .every() method. .every is defined at Array.prototype.
You can use .toArray() to convert jQuery object to an Array, within .every() callback function pass current DOM element to jQuery() to get jQuery object representation of element where .hasClass() can be chained.
function submitEnabler(inputs) {
console.log(inputs.toArray().every(isValid));
}
function isValid(input) {
return $(input).hasClass('is_glowing_success');
}
I will suggest you use array.map()
for example where input is the array
input.map(function(input){
return $(input).hasClass('is_glowing_success');
});
this is just an example read more here
Code version 1:
var one = "one";
$.each($(".Buttons"),(function() {
if ($("this:contains(one)")) {
$(this).addClass( "currentButton" );
};
}));
Code version 2:
var one = "one";
$(".Buttons").each(function(a, e) {
if ($("e:contains(one)"))
{ $(e).addClass( "currentButton" ); };
});
I think you see what I'm trying to do. My problem is only updating the specific element where the text is matched, but all elements are updated when only one matches.
Edit: HTML below:
<input type="submit" class="Buttons" value="one">
<input type="submit" class="Buttons" value="two">
<input type="submit" class="Buttons" value="one & two">
I am using inputs as they are programmatically added buttons using asp.net/c#
I have attempted a couple of solutions and I'm still having every element have the class added.
I updated this jsfiddle with inputs and now it's not being affected at all. I'm guessing :contains won't check input value.
Using my original method I fixed it as follows, or alternatively see the answer from Elias:
$(".Buttons").each(function() {
if ($(this).attr("value") == one) {
$(this).addClass("currentButton");
};
});
The easiest way to do what you're doing is a one-liner, though:
var one = "one";
$(".Buttons").filter(":contains('"+one+"')").addClass("currentButton");
//or
$(".Buttons").filter(":contains('one')").addClass("currentButton");
check fiddle
This does imply your :contains selector is a constant value, if you need it to change according to somethign else, wrap it in a function:
function changeClass(contains)
{
contains = contains || 'one';//default value
$('.Buttons').filter(":contains('"+contains+"')").addClass('currentButton');
}
Of course, as always, you can change everything to parameters so this function is more likely to be reusable:
function changeClass(selector, filter, newClass)
{
$(selector).filter(filter).addClass(newClass);
}
changeClass('.Buttons', ':contains("one")', 'currentButton');
Your problems were being caused by enclosing either this or e inside the string delimiters, which effectively turned them into string constants, not references to the DOM nodes you were trying to change
What happened is that both:
if ($("e:contains(one)"))
{
$(e).addClass( "currentButton" );
}
and
if ($("this:contains(one)"))
{
$(this).addClass( "currentButton" );
}
evaluated to :
if ([])
{
$(this).addClass('currentButton');
}
In other words: you were passing string constants to the main jQuery function ($()) which probably tried to make the bet of things, and likely treated them as selectors. Sadly, they came up empty, so an empty array-like jQuery object was returned, and any object/array is a truthy value in JS, so the expressions checked evalueted to true, hence, nodes classes were changed.
You could just as well have written:
if ($('foobar, see if it matters what I write here'))
{
console.log('It doesn\'t');
}
And it'll log It doesn't time and time again.
Edit
In response to your comment, if what you actually want is to filter, based on the elements' value attribute:
$('.Buttons').filter(function()
{//this function will be applied to each element returned by the $('.Buttons') selector
//only those for which this callback returns true will get the currentButton class
return /\bone\b/i.test($(this).val());
//or, equally valid:
return ($(this).val().indexOf('one') !== -1);
}).addClass('currentButton');
Note that /\bone\b/i will accept "ONe" as well as "One" or "one", but won't return true if the value attribute contains "bones", whereas indexOf is CaseSensitive, but doesn't check if one is part of a word or not.
for a case sensitive regex, you can use /\bone\b/, without the i.
A more strict, but lot shorter version of the same thing could be:
$('.Buttons[value~="'+one+'"]').addClass("currentButton");
But there are so many jQ selectors that you can use, best keep a reference close to your chest
DEMO
Using is():
var one = "one";
$(".Buttons").each(function () {
if ($(this).is(":contains('"+one+"')")) {
$(this).addClass("currentButton");
};
});
You can do this pretty simply without an each function. jQuery selectors will do the filtering for you, and then you update every element that matched the selector.
var text = "text to test for";
$(".Buttons:contains(" + text + ")").addclass("currentButton");
Here is a jsFiddle to demonstrate.
$("e:contains(one)") here one is being considered as a string literal.
try like, "e:contains('"+one+"')" now one will be evaluated to your string.
var one = "one";
$(".Buttons").filter(function () {
return $(":contains('"+one+"')",this);
}).addClass("currentButton");
or
var one = "one";
$(".Buttons").filter(function () {
return this.innerHTML == 'one'; // note: innerHTML may be deprecated soon, avoid it
}).addClass("currentButton");
jsFiddle demo
I'm looking for a way to do the following:
$("#a" || "#b").val() === ""
as opposed to:
$("#a").val() === "" || $("#b").val() === ""
Any ideas?
Thanks in advance!
For two elements, I believe your example is about as short as you can make it and its meaning is clear. However, if you wish to repeat such logic or evaluate more elements, you might be able to improve upon it by creating a simple function to evaluate if any items in a set match a condition.
Extending jQuery
$.fn.any = function (evaluator) {
var items = $(this);
for (var i = 0; i < items.length; i++) {
if (evaluator(items[i]) === true) {
return true;
}
}
return false;
};
Demo: http://jsfiddle.net/xE76y/1/
This is similar to the Any() method implemented in the .Net LINQ library* (and I'm sure is implemented in other libraries, especially those geared towards functional programming). In c#, you would call such a method:
enumerable.Any( o => o.Value == "" );
JavaScript's syntax (sadly) isn't as concise; you end up with something like:
array.any( function(o){ return o.value === ""; } );
So far, this hasn't saved you anything. However, if you want to iterate over a large number of elements, it becomes much more elegant.
// there could be zero inputs or 100 inputs; it doesn't matter
var result = $("input").any(function (o) {
return o.value === "";
});
Native Solution
Note that we aren't relying on jQuery in our any() method. You could also consider a native JavaScript solution such as the Array.some() method.
some() executes the callback function once for each element present in
the array until it finds one where callback returns a true value. If
such an element is found, some immediately returns true.
Demo: http://jsfiddle.net/xE76y/2/
var result = jQuery.makeArray($("input")).some(function (o) {
return o.value === "";
});
Since this is an array method, it only works on an array. This unfortunately means that document.getElementsByTagName("input").some(...) will not work since getElementsByTagName() returns a NodeList.
Of course, you could push whatever you wanted into an array and call some() on that array. The call to jQuery.makeArray() in the example is just for convenience.
Abstracting the Evaluation Functions
Demo: http://jsfiddle.net/xE76y/3/
Perhaps the evaluation functions (such as testing for an empty string) will be reused. These can be abstracted further.
// ideally, this should NOT be left in global scope
function isEmpty(input) {
return input.value === "";
}
// the check now fits nicely in one line.
if ($("input").any(isEmpty)) {
alert("At least one input is empty.");
}
The resulting method calls are quite clean: $("#a, #b").any(isEmpty) and $("input").any(isEmpty)
* Also worth noting that LINQ has been recreated for JavaScript.
Try like this instead:
if ($('#a,#b').is(':empty'))
{
alert("Either a or b is Empty!");
}
Try my demo
Edit:
If it is an input type like a textbox then it would be a little bit bulky but will achieve the same effect:
if ($.inArray("",[ $("#a").val(), $("#b").val() ])>=0)
{
alert("Either a or b is Empty!");
}
See another Demo
If you want to avoid duplication of the empty string "", you could do this:
if ($.inArray([ $("#a").val(), $("#b").val() ], ""))
Or if you only want to select once with jQuery:
if ($.inArray($("#a, #b").map(function() { return this.value; }), ""))
But I wouldn't use either of these myself. They are arguably both less efficient, more contrived, and certainly less readable than the "easy" way!
I'm not an expert in javaScript, but have you cross checked with :
http://api.jquery.com/multiple-selector/
jQuery selector regular expressions
Also, one way would be using the .each function as in
jQuery Multiple ID selectors
I always wondered why jQuery returns true if I'm trying to find elements by id selector that doesnt exist in the DOM structure.
Like this:
<div id="one">one</div>
<script>
console.log( !!$('#one') ) // prints true
console.log( !!$('#two') ) // is also true! (empty jQuery object)
console.log( !!document.getElementById('two') ) // prints false
</script>
I know I can use !!$('#two').length since length === 0 if the object is empty, but it seems logical to me that a selector would return the element if found, otherwise null (like the native document.getElementById does).
F.ex, this logic can't be done in jQuery:
var div = $('#two') || $('<div id="two"></div>');
Wouldnt it be more logical if the ID selector returned null if not found?
anyone?
This behaviour was chosen because otherwise jQuery would regularly throw NullReference Exceptions
Almost all jQuery functions return a jQuery object as a wrapper around the Dom elements in question, so you can use dot notation.
$("#balloon").css({"color":"red"});
Now imagine $("#balloon") returned null. That means that $("#balloon").css({"color":"red"});
would throw an error, rather than silently doing nothing as you would expect.
Hence, you just gotta use .length or .size().
This is just how jQuery works.
$("#something")
Object 0=div#something length=1 jquery=1.2.6
$("#nothing")
Object length=0 jquery=1.2.6
You can come close to doing what you want by accessing the length the element, and combine with the ternary operator:
console.log(!!$('#notfound').length); // false
console.log(!!$('#exists').length); // true
var element= $('#notfound').length ? $('#notfound') : $('#exists');
console.log(element.attr('id')); // outputs 'exists'
As to the heart of the question:
Wouldnt it be more logical if the ID
selector returned null if not found?
No, not for the JQuery way of doing things - namely, to support chaining of JQuery statements:
$('#notfound').hide("slow", function(){
jQuery(this)
.addClass("done")
.find("span")
.addClass("done")
.end()
.show("slow", function(){
jQuery(this).removeClass("done");
});
});
Even though notfound doesn't exist this code will run without stopping script execution. If the initial selector returns null, you'll have to add in an if/then block to check for the null. If the addClass, find, end and show methods return null, you'll have to add an if/then block to check the return status of each. Chaining is an excellent way to handle program flow in a dynamically typed language like Javascript.
It returns true because to Javascript it is a defined object therefore not false, and jQuery will always give you a new object regardless of whether the element is found or not - however the array length will be zero, e.g.
$("span").length
If you have no <span>, this will be zero, but it could be 1 or more.
You can write your own plugin to avoid repeated if statements as a Jquery plugin, like I did for this one. It's fairly easy to do:
(function($)
{
/* Checks if a jQuery object exists in the DOM, by checking the length of its child elements. */
$.fn.elementExists = function()
{
/// <summary>
/// Checks if a jQuery object exists in the DOM, by checking the length of its child elements.
/// </summary>
/// <returns type="Boolean" />
return jQuery(this).length > 0;
};
})(jQuery);
Usage:
if ($("#someid").elementExists())
{
}
You could check the .length property of the jQuery object. Like this:
if($("#two").length > 0) { // exists...
} else { // doesn't exist
}
In short, you could think of the jQuery selector return value as a group containing 0..n elements, but never being null.
What you're probably really interested in is $("#two")[0], which will give you the first actual element returned by the selector.