this is my html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Something</title>
</head>
<body>
Try Me!
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
<script>
$("a").click(function(event){
for(id=0;id<=10;id++){
setTimeout(function() {
var local_id = id;
window.open("http://www.mysite.com/characterID="+local_id,"", "win"+local_id, "width=100,height=100,resizable");
}, 3000*id);
}
event.preventDefault();
});
</script>
</body>
</html>
This link is opening each window 3 seconds after the next.
This is the row I need: http://www.mysite.com/characterID=1, http://www.mysite.com/characterID=2, http://www.mysite.com/characterID=3...
But it always opens http://www.mysite.com/characterID=11
How can I fix it?
Thank you...
This is a common issue.
You're overwriting local_id in the loop, and always referring to the same variable when the code runs. This is because JavaScript does not have block scope, just function scope.
So to scope the id, you need to invoke a function, and define the variable (or function parameter) there.
function createWindow(local_id) {
setTimeout(function () {
window.open("http://www.mysite.com/characterID=" + local_id, "", "win" + local_id, "width=100,height=100,resizable");
}, 3000 * local_id);
}
for (id = 0; id <= 10; id++) {
createWindow(id);
}
Or a similar patter would be to have the function return a function to the loop.
function createWindow(local_id) {
return function() {
window.open("http://www.mysite.com/characterID=" + local_id, "", "win" + local_id, "width=100,height=100,resizable");
};
}
for (id = 0; id <= 10; id++) {
setTimeout( createWindow(id) , 3000 * id);
}
It is because var local_id = id; is called after the loop exits when id is set to 11.
Try this:
for(id=0;id<=10;id++){
setTimeout('window.open("http://www.mysite.com/characterID='+id+'","win'+id+'","width=100,height=100,resizable")', 3000*id);
}
This is a classic JavaScript closure issue. The anonymous function is using the value id which is set to 11 at the end of the loop. To fix this, you need to make a function that returns a function (that will close around the id value).
Try it like so:
$("a").click(function(event) {
var timeout = function(local_id){
return function(){
window.open("http://www.mysite.com/characterID=" + local_id, "", "win" + local_id, "width=100,height=100,resizable");
};
};
for (id = 0; id <= 10; id++) {
setTimeout(timeout(id), 3000 * id);
}
event.preventDefault();
});
You can pass id as an additional parameter to setTimeout, like so: (This is essentially #mrk's answer with less evil.)
setTimeout(function(local_id) {
window.open("http://www.mysite.com/characterID="+local_id,"", "win"+local_id, "width=100,height=100,resizable");
}, 3000*id, id);
After lots of years I wanted to answer my own question. I couldnt remember what I was trying to do but my solution should using let not var. let is scope bounded while var is not.
for(id=0;id<=10;id++){
let local_id = id;
setTimeout(function() {
console.log(local_id)
}, 3000);
}
Related
I have nested function in function of JavaScript and I want to call it in my main function photoGallery() of my html code, but didn't work. Where I'm wrong ?
JavaScript:
function photoGallery1() {
kartinki = new Array('images/green_salad1.png', 'images/green_salad2.png', 'images/green_salad3.png');
index = 0;
function next() {
index++;
if ( index >= kartinki.length) index = 0;
document.getElementById('image2').src = kartinki[index];
}
function previous() {
index--;
if ( index < 0) index = kartinki.length -1;
document.getElementById('image2').src = kartinki[index];
}
function start() {
index = 0;
document.getElementById('image2').src = kartinki[index];
}
}
The HTML code:
<!Doctype html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width">
<meta charset="utf-8">
<title>The right eating of employed people</title>
<link rel='stylesheet' media='screen and (max-width: 1000px)' href='css/narrow.css'/>
<link rel='stylesheet' media='screen and (min-width: 1001px) and (max-width: 1235px)' href='css/medium.css' />
<link rel='stylesheet' media='screen and (min-width: 1236px)' href='css/wide.css' />
<link rel="stylesheet" href="css/calendarview.css">
<script src="js/photogallery.js"></script>
<script src="js/prototype.js"></script>
<script src="js/calendarview.js"></script>
<script type="text/javascript">
window.onload = function() {
Calendar.setup({
parentElement : 'calendar'
})
window.onload = photoGallery1()
}
</script>
First:
window.onload = photogallery1();
Results in an undefined value for the window.onload property.
As #mrbinky3000 said, you need to call photogallery1() in your onload event handler.
Furthermore, you need an object with public methods in order to make this accessible from an outside scope, in which case you need a Constructor Function:
function Photogallery() {
// Don't forget the "var" directive to prevent these from being global
var kartinki = new Array('images/green_salad1.png', 'images/green_salad2.png', 'images/green_salad3.png');
var index = 0;
this.next = function () {
index++;
if ( index >= kartinki.length) index = 0;
document.getElementById('image2').src = kartinki[index];
}
this.previous = function () {
index--;
if ( index < 0) index = kartinki.length -1;
document.getElementById('image2').src = kartinki[index];
}
this.start = function () {
index = 0;
document.getElementById('image2').src = kartinki[index];
}
}
Now your onload changes a little:
var photoGallery = null;
window.onload = function () {
// the other stuff you had
photoGallery = new Photogallery();
}
Don't forget to declare the photoGallery variable to avoid it being an implicitly declared global variable.
Now a little HTML to call the methods on your object:
<button type="button" onclick="photoGallery.next()">Next</button>
<button type="button" onclick="photoGallery.previous()">Previous</button>
I think this is what you were going for. That window.onload = photoGallery1() inside of the window.onload callback made no sense to me.
window.onload = function() {
Calendar.setup({
parentElement : 'calendar'
});
photoGallery1();
}
This will call the photoGallery1() function when the window.onload event fires. There are a lot of issues, however, with your script. Lots of things to improve.
First of, you are assigning the executed photoGallery1() function to window.onload, so basically the result of photoGallery1(). You need to assign the function itself:
window.onload = photoGallery1;
In your function photoGallery1() there is no functions being executed or returned. When we refer to scope, it means where certain functions and variables is visible from.
If you look at the functions inside photoGallery1, they are inside the scope of photoGallery1 and can not be accessed or executed from the outer scope.
One possible solution would be to do:
function photoGallery1() {
function start() {
// do your things
}
// invoke function
start();
}
window.onload = photoGallery1;
Another is to expose some of your funnctions by returning some of the functions that you need:
function photoGallery1() {
function start() {
// do your things
}
function next(){};
function previous(){};
return {
start: start,
next: next,
previous: previous
}
}
// Execute your functions
var photoGallery = photoGallery1();
window.onload = photoGallery.start;
photoGallery1() should be instantiated
var Gallery = new photoGallery1();
Functions you declared in the body of photoGallery1() are private, so you have to attach them to events inside photoGallery1.
You can look at the function as the Class and Constructor in one. So use it accordingly.
I'm not exactly sure what you are trying to accomplish, but if it is a photo gallery like my common sense would indicate, then these three things may help.
Remove any superfluous information from your example, as it confuses the issue you are trying to solve. (f.e. the calendar.js and CSS stylesheet calls). This will allow others to help you in a more effective manner.
Separate your function from your form. It is generally good practice to use HTML strictly for the skeleton of the web page/app and keep the abilities of the skeleton (the functions the page/app can do) in the javascript. This is demonstrated in my example.
Instead of nesting functions try turning your "photogallery" into an object and assigning the "next", "previous", and "start" methods to the appropriate event. (In my example I assigned "next" and "previous to buttons and "start" to window.onload)
The External Javascript File: "photogallery.js"
/*
* Wrapping code in an iife is always good practice to prevent the pollution of the global
* name space.
*/
(function(){
/*
* Declare your array of images and index outside of the photoGallery1 object so the
* methods of photoGallery1 can cleanly reference them before photoGallery1 is initialized
* in the global execution context
*/
var kartinki = ['images/green_salad1.png', 'images/green_salad2.png', 'images/green_salad3.png'];
var index = 0;
var photoGallery1 = {
next: function (){
index++;
/*
* Here the index will never be greater than kartinki.length so check against
* (kartinki.lenghth - 1) or use index == kartinki.length
*/
if (index > (kartinki.length - 1)) { index = 0 };
document.getElementById('image2').src = kartinki[index];
},
previous: function() {
index--;
if ( index < 0) { index = kartinki.length - 1 };
document.getElementById('image2').src = kartinki[index];
},
start: function() {
document.getElementById('image2').src = kartinki[index];
}
}
/*
* Do the below inside an external javascript file rather than the html you can set the
* window object's onload property to an anonymous function, in which you can call any
* functions you want to happen when the page loads (i.e. photoGallery1.start() is called).
*/
window.onload = function(){
photoGallery1.start()
}
//Setting the "next" and "previous" methods to there corresponding buttons
document.getElementById('prev').onclick = photoGallery1.previous
document.getElementById('next').onclick = photoGallery1.next
})()
The HTML file: "index.html"
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>The right eating of employed people</title>
</head>
<body>
<img id="image2">
<button id="prev">Previous</button> <button id="next">Next</button>
<!-- Add the script tag at the bottom of the body so the browser can render the
html elements referenced in photogallery.js before they are needed. If you don't do
this document.getElementById("image2") will return null, as it has not been created
at the time of photogallery.js's execution.
-->
<script src="/photogallery.js"></script>
</body>
</html>
If you have any questions please don't hesitate to ask! :D
If I got, you want the 3 functions inside photoGallery1() to be called only when photoGallery1() is called. If it is the point, just call them at the end before to close.
function photoGallery1() {
kartinki = new Array('images/green_salad1.png', 'images/green_salad2.png', 'images/green_salad3.png');
index = 0;
function next() {
index++;
if ( index >= kartinki.length) index = 0;
document.getElementById('image2').src = kartinki[index];
}
function previous() {
index--;
if ( index < 0) index = kartinki.length -1;
document.getElementById('image2').src = kartinki[index];
}
function start() {
index = 0;
document.getElementById('image2').src = kartinki[index];
}
next();
previous();
start();
}
Thank you to all for the helping and tips! :) Already is working fine and how I wanted! Lastly I post the final codes.
HTML:
<!Doctype html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width">
<meta charset="utf-8">
<title>Правилното хранене на заетите хора</title>
<link rel='stylesheet' media='screen and (max-width: 1000px)' href='css/narrow.css'/>
<link rel='stylesheet' media='screen and (min-width: 1001px) and (max-width: 1235px)' href='css/medium.css' />
<link rel='stylesheet' media='screen and (min-width: 1236px)' href='css/wide.css' />
<link rel="stylesheet" href="css/calendarview.css">
<script src="js/photogallery.js"></script>
<script src="js/prototype.js"></script>
<script src="js/calendarview.js"></script>
<script type="text/javascript">
window.onload = function() {
Calendar.setup({
parentElement : 'calendar'
})
photoGallery = new Photogallery();
}
</script>
<body>
......
<p id="photogallery">
<img src="images/prev.png" border="0"><img src="images/home.png" border="0" onclick="photoGallery.start()"><img src="images/next.png" border="0">
</p>
....
</body>
</html>
JavaScript code:
function Photogallery() {
var kartinki = new Array('images/green_salad1.png', 'images/green_salad2.png', 'images/green_salad3.png');
var index = 0;
this.next = function () {
index++;
if ( index >= kartinki.length) index = 0;
document.getElementById('image2').src = kartinki[index];
}
this.previous = function () {
index--;
if ( index < 0) index = kartinki.length -1;
document.getElementById('image2').src = kartinki[index];
}
this.start = function () {
index = 0;
document.getElementById('image2').src = kartinki[index];
}
}
var photoGallery = null;
window.onload = function () {
photoGallery = new Photogallery();
}
OK so I am making a reaction tester, and I have a function that makes shapes appear on screen, So what I want is some sort of function were after 5 clicks on a certain element it will end a function. Is there a way of doing that? sorry if its a dumb question, its because I am new to the whole coding...
Here you go
var clickHandler = (function (e) {
var count = 0;
return function () {
count += 1;
if (count > 5) {
return;
}
// do other stuff here
}
}());
aDiv.addEventListener('click', clickHandler, false);
You Can use static variable to count how many times the object has been clicked.
and here is how you can create static variable in javascript.
You can unbind the click event once the counter reaches 5. See the example below
function test(sender) {
sender.dataset.clicked++;
console.log("I've been clicked", sender.dataset.clicked);
if (+sender.dataset.clicked === 5) {
// unbind the event
sender.onclick = null;
}
return;
}
<div onclick="test(this);" data-clicked="0">click me</div>
You may use global variable which may remain counting on click function
<script>
var globalvar = 0;
onclickfunct()
{
globalvar += 1;
if(globalvar == 5)
{
//do my work
}
else
{
//give alert
}
}
</script>
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();
});
});
what I got now is:
function()
{
setInterval("getSearch()",10000);
getSearch();
}
);
But I want this interval to pause if the mouse cursor is placed inside a div on my website. How do I attack this problem? Surely I need to give the div an ID.. But some input on how to make the javascript/jquery part is much appreciated.
EDIT: More of my code.. I'm not quite sure where to insert the code in the answers inside this:
$(
function()
{
setInterval("getSearch()",10000);
getSearch();
}
);
TwitterCache = {};
function getSearch()
{
var url = "http://search.twitter.com/search.json?q=test&refresh=6000&callback=?"; // Change your query here
$.getJSON
(
url,
function(data)
{
if( data.results ) // Checks to see if you have any new tweets
{
var i = -1, result, HTML='', HTML2='';
while( (result = data.results[++i]) && !TwitterCache[result.id] )
{
insert html.. blabla}
setInterval returns a "reference" to that interval you set up, allowing you to stop it with window.clearInterval(), and that's what you have to do:
var myInterval;
function startMyInterval() {
if (!myInterval) {
// It's better to call setInterval width a function reference, than a string,
// also always use "window", in case you are not in its scope.
myInterval = window.setInterval(getSearch, 10000);
}
}
function stopMyInterval() {
if (myInterval) {
window.clearInterval(myInterval);
}
}
startMyInterval(); // Start the interval
jQuery("#myDiv").hover(stopMyInterval, startMyInterval);
Set a global variable
var intID;
Assign setInterval to this variable
intID = setInterval("getSearch()",10000);
Set an id for the div
$("#divid").hover(function(){
clearInterval(intID);
},
function(){
// set the interval again
});
I think this should work:
$("#divID").hover(
function () {
PauseTheInterValThing()
},
function()
{
setInterval("getSearch()",10000);
getSearch();
}
);
The simplest way, and the shortest
Simplest method would be:
<div id="yourDiv">
EXAMPLE TEXT
</div>
<script language="Javascript">
var interval = setInterval("getSearch()",1000);
document.getElementById("yourDiv").addEventListener('mouseover', function()
{
clearInterval(interval);
},false);
document.getElementById("yourDiv").addEventListener('mouseout', function()
{
interval = setInterval("getSearch()",1000);
},false);
</script>
insert this in your dom-ready function:
var inv = setInterval("getSearch",1000);
$('#yourdiv').mouseover(function(){
clearInterval(inv);
}).mouseout(function(){
inv = setInterval("getSearch",1000);
})
Is there a best-practice or common way in JavaScript to have class members as event handlers?
Consider the following simple example:
<head>
<script language="javascript" type="text/javascript">
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked;
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
</script>
</head>
<body>
<input type="button" id="btn1" value="Click me" />
<script language="javascript" type="text/javascript">
var btn1counter = new ClickCounter('btn1');
</script>
</body>
The event handler buttonClicked gets called, but the _clickCount member is inaccessible, or this points to some other object.
Any good tips/articles/resources about this kind of problems?
ClickCounter = function(buttonId) {
this._clickCount = 0;
var that = this;
document.getElementById(buttonId).onclick = function(){ that.buttonClicked() };
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
EDIT almost 10 years later, with ES6, arrow functions and class properties
class ClickCounter {
count = 0;
constructor( buttonId ){
document.getElementById(buttonId)
.addEventListener( "click", this.buttonClicked );
}
buttonClicked = e => {
this.count += 1;
console.log(`clicked ${this.count} times`);
}
}
https://codepen.io/anon/pen/zaYvqq
I don't know why Function.prototype.bind wasn't mentioned here yet. So I'll just leave this here ;)
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked.bind(this);
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
A function attached directly to the onclick property will have the execution context's this property pointing at the element.
When you need to an element event to run against a specific instance of an object (a la a delegate in .NET) then you'll need a closure:-
function MyClass() {this.count = 0;}
MyClass.prototype.onclickHandler = function(target)
{
// use target when you need values from the object that had the handler attached
this.count++;
}
MyClass.prototype.attachOnclick = function(elem)
{
var self = this;
elem.onclick = function() {self.onclickHandler(this); }
elem = null; //prevents memleak
}
var o = new MyClass();
o.attachOnclick(document.getElementById('divThing'))
You can use fat-arrow syntax, which binds to the lexical scope of the function
function doIt() {
this.f = () => {
console.log("f called ok");
this.g();
}
this.g = () => {
console.log("g called ok");
}
}
After that you can try
var n = new doIt();
setTimeout(n.f,1000);
You can try it on babel or if your browser supports ES6 on jsFiddle.
Unfortunately the ES6 Class -syntax does not seem to allow creating function lexically binded to this. I personally think it might as well do that. EDIT: There seems to be experimental ES7 feature to allow it.
I like to use unnamed functions, just implemented a navigation Class which handles this correctly:
this.navToggle.addEventListener('click', () => this.toggleNav() );
then this.toggleNav() can be just a function in the Class.
I know I used to call a named function but it can be any code you put in between like this :
this.navToggle.addEventListener('click', () => { [any code] } );
Because of the arrow you pass the this instance and can use it there.
Pawel had a little different convention but I think its better to use functions because the naming conventions for Classes and Methods in it is the way to go :-)