Event doesn't get added in a for-loop - javascript

This is the html. If a link is clicked I want to replace the span-element in front of it with some text.
<p><span id="sp1">that1</span> Update1</p>
<p><span id="sp2">that2</span> Update2</p>
<p><span id="sp3">that3</span> Update3</p>
<p><span id="sp4">that4</span> Update4</p>
<p><span id="sp5">that5</span> Update5</p>
As you can see, my idea was to give the spans en the anchors identical id's and a number.
In my jquery-code I loop through all the anchor-elements, give them a click-event that causes the span-element in front of it to be replaced.
<script type="text/javascript" >
$(document).ready(function() {
var numSpans = $("span").length;
for (n=0;n<=numSpans;n++) {
$("a#update" + n).click(function(e){
$('span#sp' + n).replaceWith('this');
e.preventDefault();
});
}
});
</script>
For some reason this does not work.
What am I doing wrong?

The problem with your original code is that you're creating a closure on the variable n. When the event handler is called, it is called with the value of n at the point of invocation, not the point of declaration. You can see this by adding an alert call:
$(document).ready(function() {
var numSpans = $("span").length;
for (n = 1; n <= numSpans; n++) {
$("a#update" + n).click(function(e) {
alert(n); //Alerts '6'
$('span#sp' + n).replaceWith('this');
e.preventDefault();
});
}
})
One way to fix this is to create a closure on the value of n in each iteration, like so:
$(document).ready(function() {
var numSpans = $("span").length;
for (n = 1; n <= numSpans; n++) {
$("a#update" + n).click(
(function(k) {
return function(e) {
alert(k);
$('span#sp' + k).replaceWith('this');
e.preventDefault();
}
})(n)
);
}
})
However, this is messy, and you'd do better to use a more jQuery-y method.
One way to do this would be to remove the ids from your code. Unless you need them for something else, they're not required:
<p><span>that1</span> Update1</p>
<p><span>that2</span> Update2</p>
<p><span>that3</span> Update3</p>
<p><span>that4</span> Update4</p>
<p><span>that5</span> Update5</p>
jQuery:
$(function() {
$('a.update').live('click', function() {
$(this).siblings('span').replaceWith("Updated that!");
});
});
jsFiddle

Don't create functions in a loop. With jQuery, there's no need for an explicit loop at all.
$(function()
{
$('span[id^=sp]').each(function(n)
{
$('#update' + n).click(function(e)
{
$('#sp' + n).replaceWith(this);
return false;
});
});
});
Demo: http://jsfiddle.net/mattball/4TVMa/
You can do way better than that, though:
$(function()
{
$('p > a[id^=update]').live('click', function(e)
{
$(this).prev().replaceWith(this);
return false;
});
});
Demo: http://jsfiddle.net/mattball/xySGW/

Try this:
$(function(){
$("a[id^='update']").click(function(){
var index = this.id.replace(/[^0-9]/g, "");
$("span#sp" + index).replaceWith(this);
e.preventDefault();
});
});

Related

Stop incremental counter and disable interaction

I am trying to get the counter to stop at 0 and when it does the entire div is unclickable/disable interaction.
There is a link in the middle. So when i click 3 times I want it to be un-clickable.
edit:also it doesn't need to use Knockout. any approach, if more simple is fine.
What would be the best approach?
Fiddle
var ClickCounterViewModel = function() {
this.numberOfClicks = ko.observable(3);
this.registerClick = function() {
this.numberOfClicks(this.numberOfClicks() - 1);
};
this.hasClickedTooManyTimes = ko.computed(function() {
return this.numberOfClicks() <= 0;
}, this);
};
ko.applyBindings(new ClickCounterViewModel());
Simply try adding this line
if (this.numberOfClicks() > 0)
Before
this.numberOfClicks(this.numberOfClicks() - 1);
We'll get something like that:
var ClickCounterViewModel = function() {
this.numberOfClicks = ko.observable(3);
this.registerClick = function() {
if (this.numberOfClicks() > 0)
this.numberOfClicks(this.numberOfClicks() - 1);
};
this.hasClickedTooManyTimes = ko.computed(function() {
return this.numberOfClicks() <= 0;
}, this);
};
ko.applyBindings(new ClickCounterViewModel());
See Fiddle
A bit late but give a look to my solution because I simplified a lot your code and I think you can get some value from it (for example the use of var self = this which is a best practice).
The idea behind my solution is very simple:
1) Show or hide the link or a normal text with respect to your hasClickedTooManyTimes computed variable.
empty link
<p data-bind='if: hasClickedTooManyTimes'>empty link</p>
2) Simply block the click on div if hasClickedTooManyTimes is true.
self.registerClick = function() {
if(!self.hasClickedTooManyTimes()){
self.numberOfClicks(this.numberOfClicks() - 1);
}
};
Check the Fiddle!
Let me know if this was useful to you!
Just disable the link when your count is done:
First add an id to your link:
<a id="a1" href=#> <p>empty link</p> </a>
Next disable that id when the time is right like this in your javascript:
this.hasClickedTooManyTimes = ko.computed(function() {
if (this.numberOfClicks() < 0) {
$('#a1').attr('disabled', 'disabled'); // <--- disable it
}
return this.numberOfClicks() <= 0;
}, this);
Take a look at the fiddle for JS code, stackoverflow is not validating my code section for the JS content.
HTML
<body>
<div>You have teste clicks!</div>
<div id="demo"></div>
</body>
JS
var btnObserver = (function() {
var me= this;
var clickleft = 3;
var registerClick = function() {
if(clickleft > 0) {
clickleft--;
}
};
var isCLickable = function() {
console.log(clickleft);
return clickleft !== 0;
};
return {
registerClick: registerClick,
isCLickable: isCLickable
}
})();
document.getElementById("mybtn").addEventListener("click", function(){
var message= "Hello World";
btnObserver.registerClick();
if(!btnObserver.isCLickable()) {
message= "X Blocked!";
// return false or do anything you need here
}
document.getElementById("demo").innerHTML = message;
});
Fiddle: http://jsfiddle.net/qkafjmdp/

repeating jquery function many times

I need to improve my jquery code where I repeat my function 6 times!!!
is there away to do a loop to shorten the code ?
(function( jQuery ){
jQuery.fn.vesta = function(imgN){
var imgPath = "http://localhost:8080/mhost/media/magentohost/vesta/vesta"
var currImg = imgPath + imgN + ".png";
var targetImg = jQuery('.img-browser img');
jQuery('.img-browser img').attr('src', currImg);
}
})( jQuery );
jQuery('.vesta1').on('click', function (e) {
jQuery('.vesta1').vesta(1);
});
jQuery('.vesta2').on('click', function (e) {
jQuery('.vesta2').vesta(2);
});
jQuery('.vesta3').on('click', function (e) {
jQuery('.vesta3').vesta(3);
});
jQuery('.vesta4').on('click', function (e) {
jQuery('.vesta4').vesta(4);
});
jQuery('.vesta5').on('click', function (e) {
jQuery('.vesta5').vesta(5);
});
jQuery('.vesta6').on('click', function (e) {
jQuery('.vesta6').vesta(6);
});
You can DRY this up by using a common class, and a data attribute to specify the parameter to send to your vesta function:
<div class="vesta" data-vesta="1">1</div>
<div class="vesta" data-vesta="2">2</div>
<div class="vesta" data-vesta="3">2</div>
Then there is no need to loop at all:
$('.vesta').on('click', function (e) {
$(this).vesta($(this).data('vesta'));
});
Use a common class and a data attribute
jQuery('.vesta').on('click', function (e) {
var elem = $(this);
elem.vesta(elem.data("ind"));
});
and the HTML
<div class="vesta vesta1" data-ind="1">
Just put it into a for loop, and take advantage of the dynamic nature of JavaScript:
for (var i = 1; i <= 6; i++) {
$('.vesta' + i).on('click', (function (index) {
return function (e) {
$('.vesta' + index).vesta(index);
};
})(i));
}
I suppose you need the this reference along with some hack kind of thing
$('[class*=vespa]').on('click', function(e){
$(this).vesta(+(this.className.match(/vespa(\d+)/)[1]))
});
Here, we capture elements which have a class that matches at least vespa and then we use some bit of regex to match the digits after vespa and + unary operator changes the String version of numbers into actual numbers.
It would be quite easy if you can alter the structure of the HTML.
You would give all elements the same class, say vesta. But you also give them an attribute, say data-number. For example, like this:
<div class="vesta" data-number="4"></div>
Then, your jQuery code would be as simple as:
$(document).on({
click: function() {
var $this = $(this),
number = +$this.data('number');
$this.vesta(number);
}
}, '.vesta');
Edit:
I was a bit lazy with explaining the code snippet that I have provided an hour ago, but I am modifying my post now in response to the comments.
This code snippet will allow you to apply listeners from '.vesta1' elements to '.vestaN'
[#Variable]
NumberOfClasses - is the positive integer after 'vesta'. Eg: vesta1 ,vesta2, vesta100 ... etc
var NumberOfClasses=6;
for(var i=1;i<=NumberOfClasses;i++){
var className = '.vesta'+(i+1);
jQuery(className ).on('click', function (e) {
$(this).vesta(i);
});
}

Bind function to the event "onclick" of the elements of an array of buttons

Preamble: I'm Italian, sorry for my bad English.
This is my problem:
I want to assign a function to a set of buttons.
I need to send a parameter to the function.
this is the code that I've tried:
function test(atxt) {
var buttons = $('.tblButton');
for (var i = 0; i < buttons.length; i++) {
buttons[i].onClick(sayHello(atxt));
}
}
function sayHello(txt){alert('hello' + txt)};
...getting the following error:
Uncaught TypeError: Object #<HTMLButtonElement> has no method 'onClick'
can you tell me where I went wrong and how can I fix it?
EDIT: I need iteration because I need the 'id of the button as a parameter of the function so i need to do buttons[i].onClick(sayHello(buttons[i].id))
buttons[i].onClick(sayHello(atxt));
Supposed to be
$(buttons[i]).on('click', function() { sayHello(atxt) });
If you want to get the current button id then I think you are looking for this..
for (var i = 0; i < buttons.length; i++) {
$(buttons[i]).on('click', function() { sayHello(this.id) });
}
If you want to iterate through all of the buttons then you have to do that with .each() handler of the jquery:
$(function(){
$(".tblButton").each(function () {
$(this).click(function(){
alert($(this).attr('id'));
});
});
});
checkout the jsbin: http://jsbin.com/usideg/1/edit
Would this not work for your example: Do you have another reason for the iteration?
function test(atxt) {
$('.tblButton').on('click',function(){sayHello(atxt);});
}
function sayHello(txt){alert('hello' + txt)};
OR optionally if the elements are static and present:
function test(atxt) {
$('.tblButton').click(function(){sayHello(atxt);});
}
function sayHello(txt){alert('hello' + txt)};
Alternate approach: just change
to this style:
var txt = "fred";
var atext = "hello" + txt;
function sayHello(atext) {
alert(atext);
}
$('.tblButton').on('click', function() {
sayHello(atext);
});
//below here just to demonstrate
$('.tblButton').eq(0).click();//fires with the fred
txt = "Johnny";// new text
atext = 'hello' + txt;
$('.tblButton').eq(1).click();//fires the Johnny
see it working here:
http://jsfiddle.net/dFBMm/
SO based on your note:
this markup and code:
<button class="tblButton" id="Ruth">Hi</button>
<button class="tblButton" id="Betty">Hi again</button>
$('.tblButton').on('click', function() {
alert("Hello "+$(this).attr("id"));
});
$('.tblButton').eq(0).click();//fires with the Ruth
$('.tblButton').eq(1).click();//fires the Betty
http://jsfiddle.net/dFBMm/1/

jQuery Function - return a value with two clicks

I want to carry a value inside a JavaScript function but use jQuery to detect the click or hover state.
function foo(bar){
var choc=bar;
}
When I click foo() I want it detect the first click and the second so I can do an image swap.
Example:
function foo(bar) {
var choc=id;
$(id).click(function () {
alert('first click');
}, function () {
alert('second click');
});
}
I can only return first click. This is what I am trying to do:
An example which will not work
open <img id="5" class="swap5" src="down.png" />
<div id="box5">press the up button to close me</div>
jQuery
$(document).ready(function() {
$(".open").click(function(event){
var id = event.target.id;
$('#box' + id).slideToggle();
$(".swap"+id).attr("src", "up.png");
}, function () {
var id = event.target.id;
$('#box' + id).slideToggle();
$(".swap"+id).attr("src", "down.png");
});
});
Use .toggle instead of .click.
$(document).ready(function() {
$(".open").toggle(function(event){
event.preventDefault();
var id = event.target.id;
$('#box' + id).slideToggle();
$(".swap"+id).attr("src", "up.png");
}, function (event) {
event.preventDefault();
var id = event.target.id;
$('#box' + id).slideToggle();
$(".swap"+id).attr("src", "down.png");
});
});
Also lose the href="javascript:open(5);" in the <a> tag. Use href="#" instead.
This is a pretty hacky solution, but here is my take at it.
Declare a counter variable that is initialized at 0.
On click increment it and do a check to see if it is 2 to perform your action.
var counter = 0;
function foo(bar) {
var choc=id;
$(id).click(function () {
if(counter == 2) {
//Perform action
counter = 0; //reset counter
} else {
counter++; //Increment counter
}
});
}
Did you try using the double click event handler? Check out a description by going to: http://api.jquery.com/dblclick/
It's pretty well documented so you should find it straight forward to implement

Use Javascript string as selector in jQuery?

I have in Javascript:
for ( i=0; i < parseInt(ids); i++){
var vst = '#'+String(img_arr[i]);
var dst = '#'+String(div_arr[i]);
}
How can I continue in jQuery like:
$(function() {
$(vst).'click': function() {
....
}
}
NO, like this instead
$(function() {
$(vst).click(function() {
....
});
});
There are other ways depending on your version of jquery library
regarding to this, your vst must need to be an object which allow you to click on it, and you assign a class or id to the object in order to trigger the function and runs the for...loop
correct me if I am wrong, cause this is what I get from your question.
$(function() {
$(vst).click(function() {
....
}
})
You can use any string as element selector param for jQuery.
Read the docs for more information.
http://api.jquery.com/click/
http://api.jquery.com/
You can pass a String in a variable to the $() just the way you want to do it.
For example you can do:
var id = 'banner';
var sel = '#'+id;
$(sel).doSomething(); //will select '#banner'
What's wrong is the syntax you are using when binding the click handler. This would usually work like:
$(sel).click(function(){
//here goes what you want to do in the handler
});
See the docs for .click()
Your syntax is wrong, but other than that you will have no problem with that. To specify a click:
$(function() {
for ( i=0; i < parseInt(ids); i++){
var vst = '#'+String(img_arr[i]);
var dst = '#'+String(div_arr[i]);
$(vst).click(function (evt) {
...
});
}
})
Note that since vst is changing in the loop, your event code should also be placed in the loop.
EDIT: Assuming you want the same thing to happen for each image and each div, you could also do something like this:
$(function () {
function imgEventSpec($evt) {
// image clicked.
}
function divEventSpec($evt) {
// div clicked.
}
for (var idx = 0; idx < img_arr.length && idx < div_arr.length; idx ++) {
$("#" + img_arr[idx]).click(imgEventSpec);
$("#" + div_arr[idx]).click(divEventSpec);
}
});

Categories

Resources