Basic jQuery function - javascript

Could someone please explain, why do we need each(function (i)
what does (i) do? seems like i could be any letters.
why (i) is necessary? I don't quite understand. Many Thanks
<body>
<div>Click here</div>
<div>to iterate through</div>
<div>these divs.</div>
<script>
$(document.body).click(function () {
$( "div" ).each(function (i) {
if ( this.style.color != "blue" ) {
this.style.color = "blue";
} else {
this.style.color = "";
}
});
});
</script>

The first parameter for the callback function is the index of the iteration in the loop. i.e. the loop counter. In your case you don't need it at all, as you are not using it:
$("div").each(function() {
if (this.style.color != "blue") {
this.style.color = "blue";
} else {
this.style.color = "";
}
});
The second parameter is the element for the current iteration, i.e. the same as this is referring to. You could use that parameter instead of this. Of course, to use the second parameter you have to specify the first parameter also, even if you don't use it:
$("div").each(function(i, e) {
if (e.style.color != "blue") {
e.style.color = "blue";
} else {
e.style.color = "";
}
});

That can be any name you want, it's the index of The item, in case that's useful in the function. It's explained in the jQuery documentation. http://api.jquery.com/each/

You can call i whatever you want in the callback function, the each method will call that callback for each object that matches your each selector (In your case all divs) and will pass it to your callback parameter (i) or whatever you call so you can manipulate each of those objects for each iteration.
Hope i made myself clear,
Cheers

Well, I don't know if this is what you are asking but according to documentation
.each( function(index, Element) )
That's the signature, so i in your case would be the argument for the index of current item. So if there 10 things in your div, every time the function is called it would pass the index i for i-th element in your div.

.each is used for iterating over the collection of elements it is applied to. The first parameter (i, in your example) provides the index value of the current element within the collection.
The full signature is:
.each( function(index, Element) )
while you are using the overload of:
.each( function(index) )
And it doesn't really matter how you name the parameter in your function specification (index, i, blah, param1, etc), the index value will be passed into that parameter.
In your example, this parameter value provides no additional benefit as it is not required by your use case, however, there are plenty of use cases where it is an essential parameter.

i,j,k are a standard notation for integers. It is naturally used for looping through enumerable lists (like arrays or nodelists) where each element index is an integer. For imbricated loops the first level will use i, the second j, etc.
This is just a convention and in practice you can use other names, for example "index" as shown in the jQuery documentation that makes your code more readable. The drawback is that it makes your script longer, but with today's widespread use of minification/compression this should not be a concern.
And of course if you don't reference the index within the loop you can just omit it.

Related

Passing in a function as an argument to another function and apply it to the passed in element

I'm trying to create some helper functions like what jQuery does but in vanilla JS.
This is what I have;
var $ = function(selector){
var elements = typeof selector == 'string' ? document.querySelectorAll(selector) : selector;
elements.each = function(){
this.forEach(function(item, index){
var element = _(this[index]);
//How do I apply the passed in function to this element??
})
};
}
It is used as such;
var x = 0;
$('.field').each(function(e){
e.addClass('item-' + x);
x++;
});
How do I use the function I am passing in above into the $ object and apply it to the passed in element?
There are A LOT of questions asked around this but not for my specific issue, and I couldn't modify them to suit my situation. Any help is appreciated.
Although epascarello posted a way to lay out the chaining you want to accomplish, I wanna also bring up that the current way you have things set up are contradictory.
One being that you define an each() method on the elements variable you create, and two being that you define the each() method to take no parameters, but then you go on to pass a function as a parameter when you're using the method. Also the way you are referencing the elements you queried is somewhat messed up.
I think you should instead restructure your code into something like:
const $ = function(selector){
let elements;
let bundle = {
get(selector){
return typeof selector == 'string' ? document.querySelectorAll(selector) : selector;
},
each(executor) {
// instead of calling forEach on 'this', call it on the elements instead
// thanks to closures, this each() method still has access to the elements that were queried
elements.forEach(function(item, index){
let tempEle = item;
// you can call the passed executor function here
executor();
});
}
}
elements = bundle.get(selector);
return bundle;
}
So you could then call it as below, where you send in an executor function to be applied each iteration thru the forEach() loop defined within each()
$('.field').each(() => {
console.log("Calling in the each() method");
});

for each element of a class, check the contents to see if it contains a string, and then update only that element if it does

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

Using loop to bind click event over several elements not working in javascript/jquery

I have a map of messages
say:
var Mapping = {
"notnow": 2,
"expensive": 3,
"not_worth_it": 4
}
i have a bunch of html elements (lets say divs with the same name)
so
<div id="notnow"></div>
,etc
now i want to attach a click handler to each of them,
i run a loop as shown below
function setThemUp(){
for(var item in Mapping)
{
$("#" + item).bind('click', function () {
Apply(Mapping[item]); });
}
}
But for some reason all of them seem to get bound to "not_worth_it":4. Not to their respective values.
I'm using Jquery 1.5.
Can someone please explain why this might be happening?
My guess is that instead of the Mapping[item] being resolved to their values, it's being passed as a reference or something, that's why since the value of item eventually points to "not worth it" all of them call the function with that value itself. Any way in which i could overcome them.
Hard coding each of them as
$("#notnow").bind('click', function () {
Apply(Mapping["notnow"]); });
$("#expensive").bind('click', function () {
Apply(Mapping["expensive"]); });
$("#not_worth_it").bind('click', function () {
Apply(Mapping["not_worth_it"]); });
does work, but i would prefer an elegant solution with a loop.
Answer
i went with the closure solution
function setThemUp(){
for(var item in Mapping)
{
$("#" + item).bind('click', (function () {
return function(temp) {
Apply(Mapping[temp]); };
})(item));
}
}
Reasons being , this was more of a why the loop didn't work rather than about optimization of the jquery , since this was afterall a representative example, not my actual code, and this was an elegant solution to that problem.
This is a classic case of scope issues: you're referencing the variable item in each of your bound handlers. The variable item changes, though -> it's being assigned all properties of the Mapping object literal, the last one being not_worth_it.
Creating a closure might help, to preserve the state of item for each callback:
for(var item in Mapping)
{
$("#" + item).bind('click', (function(currentItem)
{//IIFE, pass item as argument---------/
return function ()
{//return function, \/ access to closure scope
Apply(Mapping[currentItem]);
};
}(item)););
}
But this seems to be somewhat overkill, why not simply delegate the event, and use Mapping[$(this).attr('id')]?
I would suggest moving to this form:
Add the class mapped to your mapping divs.
HTML
<div id="notnow" class="mapped"></div>
JS
function setThemUp(){
$('.mapped').bind('click', function () {
Apply(Mapping[this.id]);
});
}
The problem is you need to write:
for (var item in Mapping)
rather than foreach.

insert random numbers into a class

If I do this:
$('.classname').html(Math.floor(Math.random()*10)+1);
All of "classname" is filled with the same number.
Is it possible to fill every instance of "classname" with a different random number?
The only possible way I can think of solving this is to go through each instance of "class name" and apply a random number one by one.
.html()
$(".classname").html(function(idx, oldValue) {
return (Math.floor(Math.random()*10)+1);
});
fiddle
the html method has an "overload" that accepts a function. The function should return the value to set the inner html to. In your case you can do:
$(".classname").html(function() {
return (Math.floor(Math.random()*10)+1);
});
the function is actually called with two arguments. The first is the index of the element in the selection and the second is the current value of the elements inner html
You can use jQuery's .each() function to iterate over each element matching the selector you provide -
$.each('.classname',function(index,elem){
var newRandomNumber = (Math.random()*10)+1;
$(elem).html(Math.floor(newRandomNumber));
});
For every iteration of the each() function, you'll have the index of the element you are on and the element itself in the elem parameters.
http://api.jquery.com/jQuery.each/
try this
$('.classname').each(function(index) {
$(this).html(Math.floor(Math.random()*10)+1);
});
Yes. The easiest way to do so would be using the jQuery each function:
$('.classname').each(function() {
$(this).html(Math.floor(Math.random()*10)+1);
});

jquery attr(name,value) method's value parameter

I'm reading Bibeault's jQuery in Action and I'm having troublel understanding the value parameter of the attr() method. The book says that the parameter can be a function whose parameters are index and previousValue. What is the purpose of these parameters? I don't understand the text's explanation.
Specifically I want to know:
Are these parameters mandatory?
What is an example of how these parameters are used?
Can I use other parameters within the function?
1) No parameters are mandatory in javascript. You use whatever amount you want. These parameters are available for you in your function.
2) examples:
Let's say you have this html:
Now, run this snippet:
$('a').attr('title', function(index, previousValue){
return previousValue + ' - An external link';
});
This will add the string " - An external link" to the end of every title.
Now, look at this:
$('a').attr('title', function(index, previousValue){
return previousValue + ' - Link number ' + index;
});
This will result in the following html:
As you can see, you can see, these parameters are very handy.
3) Not sure what you mean by using other parameters. Please clarify.
It seems that you are not familiar with Javascript's scope lookup chain. You do not have to explicitly pass parameters to a function. If they're defined in a scope above it, the function will have access to it:
var num1 = 23;
var num2 = 54;
$('a').attr('title', function(){
return num1 + num2;
});
It's actually pretty simple. The attr() function has three possible modes; the one you refer to takes a callback to get the value.
For example:
$('.someClass').attr('rel', function(index, value)
{
// index refers to the elements index of the set; so of all elements with the
// css class 'someClass', the index will refer to that position in the list.
// If three elements match, the callback will be invoked 3 times, with 0, 1, 2
// as the index when each element, respectively, is invoked.
// value refers to the current value of the attribute.
// Return the value you want to set.
return 'SomeRelValue';
});
The parameters are not mandatory; if you simply omit them from the callback signature, you simply dont have access to that information. You cannot pass other parameters to this method. You might want to use this function when you will match a lot of elements, and want to insert some data based on their ordinal position of the selector element.
For example:
$('.someElements').attr('rel', function(index, value)
{
return value + index;
});
For each element matching the selector, you set the rel attribute to what it was plus the index of the selector. So element one, if it had a rel of 'sampleRel', is set to 'sampleRel1', element two with rel' sampleRel' becomes 'sampleRel2', etc etc
There's a good example in jQuery's documentation, which I'll shorten here:
Here's the relevant HTML:
<div>Zero-th </div>
<div>First </div>
<div>Second </div>
If you then run this javascript it'll apply unique id's to each div:
$("div").attr("id", function (arr) {
return "div-id" + arr;
});
The $ function returns all of the div's so the arr argument allows you to specify the attribute value based on the index.
The function passed to attr also received a value specifying the old attribute value, but as this is javascript the function doesn't have to name that argument and it's still available in the arguments.
attr is a way to access attributes on an element. the overload which allows a function will allow you to use the function return to set the value (the index param to the function would be the index into the selection, previousValue being the value it had until now).
I've never used this overload myself, but assume it would be nice if you're making attribute values based on some sort of function.
They are not mandatory, passing different number of parameters gives different functionality:
attr('name') - gets the value of name
attr('name','value') - sets the value of name
attr('name',function(i,v){return v+i;}); - sets the value of name to the previous value + the index in the collection.
example:
lets say we have five spans with the class hiccup and name 'yup'.
$('span.hiccup').attr('name',function(i,v){return v + i;});
will give each span a name 'yup1' - 'yup5'.
Additionally, you have access to this inside the function, which refers to the element itself. Given this, you could probably do some really interesting stuff.
As always, jQuery has awesome documentation of all of this:
http://api.jquery.com/attr

Categories