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();
}
Related
This question already has answers here:
Coordinating parallel execution in node.js
(7 answers)
Closed 5 years ago.
I am implementing the IIFE method when wrapping a for a loop around an async/ajax call.
var j = 4;
for (var i = 0; i < j; i++) {
(function(cntr) {
asyncCall(function() {
console.log(cntr);
});
})(i);
}
The problem is that when I console.log cntr, I get all of the values, but they have a random order. Let's say I have a for loop from 0-4. It will print these values in a random order, like 2,1,3,4,0. This changes every time I rerun the code.
Edit:
The question linked to most certainly is not the answer. Please pay more attention before marking as duplicate. I'm also not even using nodejs...
Your asyncCall doesn't finish in a constant amount of time. Since you begin each asynchronous call within a synchronous for-loop, whichever call finishes first will log first (in other words, the calls do not wait for each other).
There are two ways to go about fixing this problem. The first is to wait for each asyncCall to complete before beginning the next, which you could do easily with recursion:
var calls = 4
var i = 0
asyncCall(function handler() {
console.log(i)
if (++i < calls) asyncCall(handler)
})
function asyncCall(handler) {
setTimeout(handler, 0)
}
This approach makes the most sense if each successive call depends on the result of the previous call, which yours might not. In that case, it is more efficient to have each call execute up-front, but store the results in an array which you log once every call has completed:
var calls = 4
var done = 0
var i = 0
var results = []
for (var i = 0; i < calls; i++) (function(i) {
asyncCall(function handler() {
results[i] = i // example data
if (++done === calls) complete()
})
})(i)
function complete () {
// Do something with your array of results
results.map(function (e) {
console.log(e)
})
}
function asyncCall(handler) {
setTimeout(handler, Math.random()*10)
}
A self-executing closer, which it looks like you're looking for, is used like this:
//<![CDATA[
/* external.js */
var doc, bod, htm, C, T, E; // for use onload elsewhere
addEventListener('load', function(){
doc = document; bod = doc.body; htm = doc.documentElement;
C = function(tag){
return doc.createElement(tag);
}
T = function T(tag){
return doc.getElementsByTagName(tag);
}
E = function(id){
return doc.getElementById(id);
}
addClassName = function(element, className){
var rx = new RegExp('^(.+\s)*'+className+'(\s.+)*$');
if(!element.className.match(rx)){
element.className += ' '+className;
}
return element.className;
}
removeClassName = function(element, className){
element.className = element.className.replace(new RegExp('\s?'+className), '');
return element.className;
}
var outs = doc.getElementsByClassName('output');
for(var i=0,out,l=outs.length; i<l; i++){
(function(){
var out = outs[i], b = false;
out.onclick = function(){
if(b){
removeClassName(out, 'blue'); b = false;
}
else{
addClassName(out, 'blue'); b = true;
}
}
}());
}
}); // close load
//]]>
/* external.css */
html,body{
padding:0; margin:0;
}
.main{
width:980px; margin:0 auto;
}
.output{
width:100px; border:1px solid #000; border-top:0;
}
.output:first-child{
border-top:1px solid #000;
}
.blue{
background:lightblue;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
<link type='text/css' rel='stylesheet' href='external.css' />
<script type='text/javascript' src='external.js'></script>
</head>
<body>
<div class='main'>
<div class='output'>Sorry</div>
<div class='output'>This</div>
<div class='output'>Example</div>
<div class='output'>Isn't</div>
<div class='output'>Better</div>
</div>
</body>
</html>
Here's how the closure works. Events actually fire when they occur, like onclick. By the time the Event fires you are actually at the end of the loop, so when that happens, vars and arguments look for the level they were last scoped to.
I want to separate my markup from the behavior but i stuck on how to associate the function i created to all the links in the document after the onclick event without including it directly in the markup. Here is my HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Image Gallery</title>
</head>
<body>
<h1>Snapshots</h1>
<ul>
<li>
Fireworks
</li>
<li>
Coffee
</li>
<li>
Rose
</li>
<li>
Big Ben
</li>
</ul>
<img id="placeholder" src="images/placeholder.gif" alt="my image gallery">
<script type="text/javascript" src="scripts/showPic.js"></script>
</body>
</html>
and here is my script
function showPic(whichPic) {
"use strict";
var source = whichPic.getAttribute("href");
var placeholder = document.getElementById("placeholder");
placeholder.setAttribute("src", source);
}
window.onload = function() {
"use strict";
var anchors = document.getElementsByTagName("a");
console.log(anchors);
for(var i = 0, count = anchors.length; i < count; i++) {
//anchors[i].preventDefault;
// anchors[i].onclick = showPic;
}
}
I tried to loop through the links and assign the function to the onclick event of each link but it didn't work. What is the best way this could be done so that behavior is completely separate from markup?
function showPic(e) {
// e is the event, e.target is the current element
"use strict";
e.preventDefault();
var source = e.target.getAttribute("href");
var placeholder = document.getElementById("placeholder");
placeholder.setAttribute("src", source);
}
window.onload = function() {
"use strict";
var anchors = document.getElementsByTagName("a");
for(var i = 0, count = anchors.length; i < count; i++) {
anchors[i].onclick = showPic;
// Or you could use anchors[i].addEventListener('click',showPic);
}
}
Don't forget to remove what's in the HTML.
JS Fiddle Demo
On a side note
Void mentioned using addEventListener instead of onclick. Both are acceptable, with a difference to keep in mind:
This code:
element.onclick = func1;
element.onclick = func2;
Will result in func2 being called on click, but not func1. That is because it overwrites any previous .onclick statement on that precise element.
This code, however:
element.addEventListener('click',func1);
element.addEventListener('click',func2);
Will result in both func1 and func2 being executed, because they add up.
Best Way
Array.prototype.forEach.call(document.getElementsByTagName("a"), function(el){
el.addEventListener("click", function(event){
event.preventDefault();
showPic();
});
});
Suppose you open a handful of windows with:
window.open(url1,'win1');
window.open(url2,'win2');
window.open(url3,'win3');
(each window has a unique name)
And then you refresh the page.
The 3 popup windows are still open. Is there a way to list the names of all of the open windows and close them?
This is not a duplicate question.
In this question the browser is being refreshed, so you cannot simply use a global array to keep track of child windows.
This is not a duplicate question.
So the questions is closed, I'll post an answer based on the comments and research.
Firstly, to all who commented, thank you for helping.
Answer:
There is not a built-in object which tracks opened windows and persists from page load to page load.
As Felix Kling pointed out, using localStorage is a possible work-around.
Try postMessage to communicate between existing windows within the same domain. That's how i'm going to try and solve the same problem. See: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
index.htm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>pop</title>
</head>
<body>
<script>
var pops = [];
window.onmessage = function(e)
{
// todo: check domain
// if( e.origin )
var data;
try
{
data = JSON.parse(e.data);
}
catch(e)
{
// fail silent...?
return;
}
switch(data.event)
{
case "RestoreOpenWindow":
onClosePopup(e.source.name);
case "QueryOpenWindows":
pops.push(e.source);
updateLabel();
break;
}
};
window.onload = function()
{
window.onClosePopup = onClosePopup;
updateLabel();
};
window.onbeforeunload = function()
{
for(var i = 0; i < pops.length; i++) pops[i].queryOpenPopups();
};
function onClosePopup(name)
{
for(var i = pops.length - 1; i >= 0; i--)
if(pops[i].name === name)
{ pops.splice(i, 1); break; }
updateLabel();
};
function openPopup()
{
pops.push(window.open("pop/popup.htm", "pop" + pops.length, ' '));
updateLabel();
setTimeout(function(){
alert('Click ok to refresh...');
location.href = location.href;
}, 5000);
}
function updateLabel()
{
document.getElementById("total").innerHTML = pops.length;
var html = [];
for(var i = 0; i < pops.length; i++)
html.push(pops[i].name);
document.getElementById("names").innerHTML = html.join("<br"+"/"+">");
}
</script>
<button onclick="openPopup()">open popup and refresh after 5 seconds (...allow em popups...)</button></br>
<span>total: </span><span id="total"></span></br>
<span id="names"></span></br>
</body>
</html>
popup.htm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>pop</title>
</head>
<body>
<script>
window.queryOpenPopups = function()
{
var count = 0;
var hInterval = setInterval(function () {
try
{
if(window.opener)
{
window.opener.postMessage(JSON.stringify({"event": "QueryOpenWindows", "name": window.name}), "*");
clearInterval(hInterval);
} else count++;
}
catch(e)
{
count++;
}
if(count > 50)window.close();
}, 100);
};
window.onbeforeunload = function(){
window.opener.onClosePopup(window.name);
};
// restore link with opener on refresh
window.opener.postMessage(JSON.stringify({"event": "RestoreOpenWindow", "name": window.name}), "*");
window.onload=function(){ document.getElementById("name").innerHTML = window.name; };
</script>
<span id="name"></span>
</body>
</html>
The issue is in the model, when recalled by the presenter it does not work like I assumed, in fact it works as if the this keyword refers to an [object HTMLTextAreaElement], not to a Model object.
/** PRESENTER **/
function Presenter() {
var toolbarView;
var outputView;
var sourceCodeModel;
var public = {
setToolbarView: function (view) {
toolbarView = view;
},
setOutputView: function (view) {
outputView = view;
},
setModel: function (_model) {
sourceCodeModel = _model;
},
init: function () {
toolbarView.setNewTableHandler(function () {
outputView.updateSource(sourceCodeModel.string);
});
toolbarView.setNewConstraintHandler(function () {
/*stub*/
alert("new constraint");
});
}
}
return public;
}
/** TOOLBAR VIEW **/
function toolbarView() {
this.setNewTableHandler = function (handler) {
$("#newTable").click(handler);
}
this.setNewConstraintHandler = function (handler) {
$("#newConstraint").click(handler);
}
}
/** OUTPUT VIEW **/
var outputView = {
updateSource: function (newVal) {
$("#sourcetext").val(newVal);
},
draw: function () {
//stub
}
};
/** MODEL **/
var model = new Object(); //o {};
model.source = [];
model.string = function () {
/* ALERT(this) returns [object HTMLTextAreaElement] wtf? */
var stringa = "";
for (var i = 0; i < this.source.length; i++) { //this does not work, since this = HTMLTextAreaElement
stringa += this.source[i] + "\n";
}
return stringa;
}
$(document).ready(function () {
var presenter = new Presenter();
var view1 = new toolbarView();
presenter.setToolbarView(view1);
presenter.setOutputView(outputView);
presenter.setModel(model);
presenter.init();
});
and the HTML is pretty simple:
<!doctype html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="mvp.js"></script>
<meta charset="UTF-8">
<title>Titolo documento</title>
<style type="text/css">
/*unnecessary here*/
</style>
</head>
<body>
<div id="container">
<div id="toolbar">
<button id="newTable">New table</button>
<button id="newConstraint">New Constraint</button>
</div>
<div id="source">
<textarea id="sourcetext"></textarea>
</div>
<button id="update">Update</button>
<div id="output"></div>
</div>
</body>
</html>
What am i doing wrong on the model object?
When you pass a function as a listener the this property will not be available inside the function:
var obj = {
a: function() {
alert(this)
}
};
$('body').click(obj.a);
When body is clicked the function's this property will be document.body.
To prevent this you must bind the function:
$('body').click(obj.a.bind(obj));
Or in older browsers wrap it:
$('body').click(function() {
obj.a();
});
So you must bind the function before pass it:
outputView.updateSource(sourceCodeModel.string.bind(sourceCodeModel));
More info about javascript function's context: http://www.quirksmode.org/js/this.html
this line: var public = { try to do not to use public, that is reserved word.
And a general note, try to bind this to a variable, because this changes context where it is currently.
/** TOOLBAR VIEW **/
function toolbarView() {
var that = this;
that.setNewTableHandler = function (handler) {
$("#newTable").click(handler);
}
that.setNewConstraintHandler = function (handler) {
$("#newConstraint").click(handler);
}
}
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);
}