Here is the link to the page with the script.
http://signsourceak.com/index1.html
Here is my script and for some reasons all the functions fire with out mouse over. Can anyone tell me what is wrong with my script
window.onload = sliding;
var tags = new Array('tag1','tag2','tag3','tag4','tag5','tag6','tag7','tag8');// List of headings
var pics = new Array('popout1','popout2','popout3','popout4','popout5','popout6','popout7','popout8');// list of images that slide out
function sliding(){ // assing event
for(var i=0; i< tags.length; i++){
document.getElementById(tags[i]).onmouseover = slideout(tags[i],pics[i]); // <-- The Problem is Here Function runs with out the actual event
document.getElementById(tags[i]).onmouseout = slidein(tags[i],pics[i]);
//alert('this worked,'+ tags[i] + pics[i]);
}
}
function slideout(hid,picid){
document.images[picid].style.visibility = "visible";
document.images[picid].style.MozOpacity = 0.7;// need browser compatability
moveout(hid,picid);
}
function moveout(hid,picid){
if(currpos(picid) > 0){
document.images[picid].style.top = currpos(picid) - 1 + "px";
setTimeout(moveout,10);
}else{
clearTimeout(moveout);
}
function currpos(element){
return document.getElementById(element).offsetTop;
}
}
function slidein(hid,picid){
document.images[picid].style.MozOpacity = 0.5;// need browser compatability
movein(hid,picid);
}
function movein(hid,picid){
if(currpos(picid) < 210){
document.images[picid].style.top = currpos(picid) + 1 + "px";
setTimeout(movein,10);
}else{
clearTimeout(movein);
document.images[picid].style.visibility = "hidden";
}
function currpos(element){
return document.getElementById(element).offsetTop;
}
}
that is not how to use clearTimeOut.
setTimeout returns a timer id that have to be passed to clearTimeOut:
var timer = setTimeout( fn, 10 );
clearTimeout( timer);
You are assigning the result of slideout() and slidein() as the handlers. You also need to isolate the closure variables; self calling functions will make sure the the i looping variable is not shared by all the event handlers
function sliding(){ // assing event
for(var i=0; i< tags.length; i++){
document.getElementById(tags[i]).onmouseover =(function(index){
return function() {
slideout(tags[index],pics[index]);
}
})(i);
document.getElementById(tags[i]).onmouseout = (function(index){
return function() {
slidein(tags[index],pics[index]);
}
})(i);
}
}
One problem is here:
function sliding(){ // assing event
for(var i=0; i< tags.length; i++){
document.getElementById(tags[i]).onmouseover = slideout(tags[i],pics[i]); // <-- The Problem is Here Function runs with out the actual event
document.getElementById(tags[i]).onmouseout = slidein(tags[i],pics[i]);
//alert('this worked,'+ tags[i] + pics[i]);
}
}
In that loop, you're calling the "slideout" and "slidein" functions, though it's obvious that that's not what you want to do. What you want is to assign a function that calls "slideout" or "slidein" the appropriate way. To do that, you'll need another layer of function:
function makeHandlers(index) {
return {
'out': function() { slideout(tags[index], pics[index]; },
'in': function() { slidein(tags[index], pics[index]; }
};
}
function sliding() {
for (var i = 0; i < tags.length; ++i) {
var handlers = makeHandlers(i), tag = document.getElementById(tags[i]);
tag.onmouseover = handlers.in;
tag.onmouseout = handlers.out;
}
}
As #BiAiB notes, your calls to "setTimeout" and "clearTimeout" need some attention too.
Related
I am writing a function of a game:
function Game(){
while(true){
***
for(var i = 0; i < level; i++){
var color;
$(".btn").on("click", function(event) {
ButtonClickResponse(this.id);
color = this.id;
});
if(colorsOrder[i] != color){
GameOver();
return;
}
}
***
}
}
the "if statement" in the loop of function runs and increments "i" immediately many times when loop is started and doesnt wait for an above event to finish.
I searched for solving with "async await" and "promise" in google and stackoverflow, but didn't really understand how it worked so couldn't implemet it in my code.
This should work, although I didn't test it and you do things not in javascript way
async function Game() {
while (true) {
var level = 1;
$("#level-title").text("Level " + level);
var colorsOrder = [];
var randColor = GenerateRandomSquareColor();
colorsOrder.push(randColor);
ButtonClickResponse(randColor);
for (var i = 0; i < level; i++) {
var color;
// await for the click event
const event = await waitForClick($(".btn"));
// do stuff with the result
ButtonClickResponse(event.target.id);
color = event.target.id;
if (colorsOrder[i] != color) {
GameOver();
return;
}
level++;
}
}
}
function waitForClick(element) {
// create new promise
return new Promise((resolve) => {
const handler = function (event) {
// when button is clicked remove listener
element.off("click", handler);
// and resolve the promise
resolve(event);
};
// listen for click
element.on("click", handler);
});
}
I work on a js project that has a nested loop and I've got into a weird situation:
When functions are in the nested loop, e.g.
window.onload = function() {
let board = document.getElementById('board');
for ( let i = 0; i < 9; i++ ) {
for ( let j = 0; j < 9; j++ ) {
const block = [];
block[i, j] = document.createElement( 'img' );
block[i, j].src = "empty-block.png";
block[i, j].coordinate_i = i;
block[i, j].coordinate_j = j;
block[i, j].addEventListener('contextmenu', function r_click(event)
{
coordinate_i = event.target.coordinate_i;
coordinate_j = event.target.coordinate_j;
block[coordinate_i, coordinate_j].src="flag.png";
event.preventDefault();
});
board.appendChild(block[i, j]);
}
}
}
code will work as expected and when the button will get clicked with right-click, the image will be replaced.
but when I move the function outside the loop and call it from the inside, e.g.
block[i, j].addEventListener('contextmenu', r_click);
the function won't work.
Can I get an explanation for these occurrences?
it does not work because the function r_click is defined inline. If you pull it out as a separate function, and bind both events using its name it will work.
This is inline definition notice that the name of the function is not required, because the function is not needed because the function exists in the handler.
block[i, j].addEventListener('contextmenu', function (event)
{
coordinate_i = event.target.coordinate_i;
coordinate_j = event.target.coordinate_j;
block[coordinate_i, coordinate_j].src="flag.png";
event.preventDefault();
});
This does the same as the code above, but the function is actually accessible from other places
function r_click(event){
coordinate_i = event.target.coordinate_i;
coordinate_j = event.target.coordinate_j;
block[coordinate_i, coordinate_j].src="flag.png";
event.preventDefault();
}
window.onload = function() {
//....code here ....
block[i, j].addEventListener('contextmenu',r_click);
//...more code here
}
I'm appending onclick events to elements that I'm creating dynamically. I'm using the code below, this is the important part only.
Test.prototype.Show= function (contents) {
for (i = 0; i <= contents.length - 1; i++) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function () { return that.ClickContent.apply(that, [contents[i]]); };
}
}
First it says that it's undefined. Then I changed and added:
var content = content[i];
menulink.onclick = function () { return that.ClickContent.apply(that, [content]); };
What is happening now is that it always append the last element to all onclick events( aka elements). What I'm doing wrong here?
It's a classical problem. When the callback is called, the loop is finished so the value of i is content.length.
Use this for example :
Test.prototype.Show= function (contents) {
for (var i = 0; i < contents.length; i++) { // no need to have <= and -1
(function(i){ // creates a new variable i
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function () { return that.ClickContent.apply(that, [contents[i]]); };
})(i);
}
}
This immediately called function creates a scope for a new variable i, whose value is thus protected.
Better still, separate the code making the handler into a function, both for clarity and to avoid creating and throwing away builder functions unnecessarily:
Test.prototype.Show = function (contents) {
for (var i = 0; i <= contents.length - 1; i++) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = makeHandler(i);
}
function makeHandler(index) {
return function () {
return that.ClickContent.apply(that, [contents[index]]);
};
}
};
A way to avoid this problem altogether, if you don't need compatibility with IE8, is to introduce a scope with forEach, instead of using a for loop:
Test.prototype.Show = function (contents) {
contents.forEach(function(content) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function() {
return that.ClickContent.call(that, content);
};
});
}
I have 3 methods
exports.getImageById = function (resultFn, id) {
...
}
exports.getCollectionById = function (resultFn, id) {
}
in the third method I want to call both methods
exports.getCollectionImages = function (resultFn, collectionId) {
var arr = new Array();
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (i = 0; i < images.length; i++) {
this.getImageById(function (result1) { // error, 2nd call
arr[i] = result1;
}, images[i]
);
}
}
, collectionId
);
resultFn(arr);
}
I can call first function this.getCollectionById but it fails to call this.getImageById, it says undefined function, whats the reason for that?
When you call this.getCollectionById passing it a callback, the callback doesn't have access to the same this
The simplest solution is to save this as a local variable.
exports.getCollectionImages = function (resultFn, collectionId) {
var arr = new Array();
var me = this; // Save this
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (var i = 0; i < images.length; i++) {
// Use me instead of this
me.getImageById(function (result1) { // error, 2nd call
arr[i] = result1;
}, images[i]);
}
}, collectionId);
resultFn(arr);
}
The value of this inside the inner function is not the same object as outside, because it's determined depending on how the function is called. You can find a detailed explanation in the MDN article on this.
One of the ways to solve it is by keeping a reference to the outer this in another variable such as that:
var that = this;
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (i = 0; i < images.length; i++) {
that.getImageById(function (result1) { // 2nd call
arr[i] = result1;
}, images[i]
);
}
}
, collectionId
);
Can someone tell me what I am doing wrong here? I simplified it below but I am basically trying to create a list and have a click event that references a variable only available in the loop.
for (var i = 0; i < data.length; i++) {
$newRow = $(rowFormat);
$('a:first', $newRow).click(function(i){
return function() { alert(i); }
});
$list.append($newRow);
}
You aren't calling the outer function.
$('a:first', $newRow).click(function(j){
return function() { alert(j); }
}(i)); /* Pay special attention to this line, it is where the major change is */
As T.J. Crowder mentioned, you can move the factory out of the loop.
function my_factory(j) {
return function() {
alert(j);
};
}
$('a:first', $newRow).click(my_factory(i));
You've almost got it, just one small change. This is actually my favorite example of a practical use of a closure in Javascript.
Basically, you need to create a function that takes a value, and returns a function that uses that value. See the commented line below for what your example is missing. Your code created the anonymous function, but didn't invoke it.
for (var i = 0; i < data.length; i++) {
$newRow = $(rowFormat);
var fn = (function (value) {
return function() {
alert(value);
};
}) (i); //this is what you were missing, you need to invoke the anonymous function
$('a:first', $newRow).click(fn);
$list.append($newRow);
}
Use 'bind' to attach the 'click' event and pass a parameter. Using 'event.data' you will be able to get the right value of your parameter:
Example 1:
$(document).ready(function()
{
for (var i = 0; i < data.length; i++)
{
$newRow = $(rowFormat);
$('a:first', $newRow).bind('click', {i: i},
function (event)
{
alert(event.data.i);
}
);
$list.append($newRow);
}
});
Example 2:
$(document).ready(function()
{
$(".selectorA").each(function()
{
var elementId = $(this).attr("id");
for(var i = 0; i < 20; i++)
{
$('#question' + i).bind('click', {currentIndex: i, elementId: elementId},
function (event)
{
alert(event.data.currentIndex + " | " + event.data.elementId);
}
);
}
}
});