onmouseout not firing properly - javascript

I have this code which is supposed to fire on mouseover and it's counterpart to do the opposite on onmouseout:
colinc();
function colinc(){
var hexnum=number.toString(16);
var hexcolor="#"+hexnum+hexnum+hexnum;
document.getElementById("c"+x).style.backgroundColor=hexcolor;
number=number+8;
if(number<=184)
setTimeout(colinc,50);
}
The counter part only has the change of number = number-8; and number>=40;
The problem is i have multiple boxes that should light up with color change on mouseover and lightdown with mouseout. when i move slowly over my boxes(large in no.) then everything is ok but when i move quickly some boxes do not light down...it looks like the onmouseout doesn't happen if i pass very quickly.
Any help?
function flash(x){
number=0;
var cc = document.getElementById("c"+x);
var cs=document.defaultView.getComputedStyle(cc,null);
var bg=cs.getPropertyValue('background-color');
var str=""+bg;
var n=str.replace("rgb","");
n=n.replace("(","");
n=n.replace(")","");
var arr=n.split(",");
number=parseInt(arr[0]);
colinc();
function colinc(){
var hexnum=number.toString(16);
var hexcolor="#"+hexnum+hexnum+hexnum;
document.getElementById("c"+x).style.backgroundColor=hexcolor;
number=number+8;
if(number<=184)
setTimeout(colinc,50);
}
}
function flashe(x){
number=0;
var cc = document.getElementById("c"+x);
var cs=document.defaultView.getComputedStyle(cc,null);
var bg=cs.getPropertyValue('background-color');
var str=""+bg;
var n=str.replace("rgb","");
n=n.replace("(","");
n=n.replace(")","");
var arr=n.split(",");
number=parseInt(arr[0]);
colinc();
function colinc(){
var hexnum=number.toString(16);
var hexcolor="#"+hexnum+hexnum+hexnum;
document.getElementById("c"+x).style.backgroundColor=hexcolor;
number=number-8;
if(number>=40)
setTimeout(colinc,40);
}
}
This is my full js code

Check whether the events fire properly by logging them in the console:
function MouseOverHandler(event) {
console.log('mouseover');
}
function MouseOutHandler(event) {
console.log('mouseout');
}
Also do you ever halt the execution of either handlers when the opposite event happens. This would be done via getting the timeout id and canceling it.
var mouseOverTimeout, mouseOutTimeout;
function colinc(){
clearTimeout(mouseOutTimeout);
mouseOverTimeout = setTimeout(colinc,50);
}
function MouseOutHandler(event) {
clearTimeout(mouseOverTimeout);
mouseOutTimeout = setTimeout(MouseOutHandler,50);
}

In your code:
> function colinc(){
>
> var hexnum=number.toString(16);
The identifier number hasn't be declared or initialised, so you get a reference error and the script fails. Before the above line, you should probably add:
var number = 0;
or give number some other value.
> var hexcolor="#"+hexnum+hexnum+hexnum;
> document.getElementById("c"+x).style.backgroundColor=hexcolor;
> number=number+8;
> if(number<=184)
> setTimeout(colinc,50);
But here you need access to a global number, so you can keep a reference in a closure or make number global. If you're going to do that, give it a better name, like *colnic_counter* or something that is unlikely to clash with some other global.
> }
Something like:
var colinc = (function() {
var num = 0;
return function() {
var hexnum = num.toString(16);
var hexcolor = "#" + hexnum + hexnum + hexnum;
// document.getElementById("c"+x).style.backgroundColor=hexcolor;
console.log(hexcolor);
num += 8;
if (num <= 184)
setTimeout(colinc,50);
}
}());
colinc();
Note that since a function expression is used to initialise the function, you have to call it afterward.

I have solved the problem of cleartimeout. I created two arrays to hold the current mouseover and mouseout setTimeout ids of every box according to their Id. Everytime a mouseout is called it first clears its corresponding mouseover from the array and same for mouseout.

Related

Accessing variables from another function is showing reference error?

I have two functions. The first function calculates variables on domcontentloaded and resize. The second function triggers on both domcontentloaded and scroll. I need 3 variables from the first function inside the second function to calculate some stuff. I am trying to get the return variable array from 1st function upme() to use inside second function doso() - I am getting this error poss isn't defined at htmldocument.doso
JAVASCRIPT
document.addEventListener('DOMContentLoaded', upme);
window.addEventListener('resize', upme);
function upme()
{
var rome = document.getElementById("out-cmnt");
var rect = rome.getBoundingClientRect();
// console.log(rect.top, rect.right, rect.bottom, rect.left);
var poss = rect.top + window.scrollY; var iwwr = window.innerWidth;
var koss = rect.bottom + window.scrollY; var loss = koss - poss;
return [poss, loss];
}
document.addEventListener('DOMContentLoaded', doso);
window.addEventListener('scroll', doso);
function doso()
{
lopp = document.getElementById("Web_1920__1");
hope = lopp.clientHeight; const meme = document.body.scrollHeight;
const keke = hope/meme; const scsc = window.scrollY;
var innr = window.innerHeight;
var saka = upme(); // problem here calling 1st function
var noss = poss - innr + loss; // problem here
if(scsc > noss && window.matchMedia("(min-width: 765px)").matches)
{
// doing something
}
}
doso();
How can I successfully get those variables like poss, loss, koss inside the second function doso() - ? Please help me out. Thanks to everyone involved in this community.
When calling a function, you can only get values you chose to return.
If you want to use them by name you need to declare them with the wanted names. In your example, you only have an array called saka with [0] being poss and [1] being loss.
First, here you can only access poss and loss because that's the only two variables you are returning in the upme function.
You return them as an array, there is an easy way in javascript to retrieve and name variables returned in an array:
var [poss, loss] = upme();
With this snippet of code, you say that the array you are returning from this function is of size 2 and that you want to declare 1 variable for each element, by naming them respectively poss and loss.
If you need more variables, just return more of them in upme then declare and name them when calling the function.
You could also create an object, but this solution is good enough for your problem.
Change this line:
var noss = poss - innr + loss;
to:
var noss = saka.poss - innr + saka.loss;

Javascript - element.addEventListener() returns "Uncaught TypeError"

For context, I am trying to code a memory game where you have to pair two of the same colored circles until the whole board is complete. I've called it Match-Two. Here is the code that I'll reference from:
class Circle {
constructor(element, circleColor){
this.elem = element;
this.color = circleColor;
}
}
var frequency = [0, 0, 0, 0, 0, 0, 0, 0];
var num;
var hue = new Array(8);
var circle = new Array(16);
hue[0] = "#0039ff";
hue[1] = "#ff0000";
hue[2] = "#43ff00";
hue[3] = "#fffa00";
hue[4] = "#7405b5";
hue[5] = "#ff9d00";
hue[6] = "#ff00c3";
hue[7] = "#00fff6";
onload = function() {
for(var i = 0; i < 16; i++){
circle[i] = new Circle(document.getElementById("circle" + i));
while(circle[i].color === undefined){
num = Math.floor(Math.random() * 8);
if(frequency[num] != 2){
frequency[num]++;
circle[i].color = hue[num];
circle[i].elem.addEventListener('click', function(){
main(circle[i])
});
}
}
}
}
function main(circle){
circle.elem.style.backgroundColor = circle.color;
}
So in this code I create a class of Circle and I create an array of Circle objects which is identified as 'circle'. When the page is loaded, I give each circle object an element reference from my html document (There are 16 circles and they each have an id of circle0, circle1, circle2.. etc. Then there's a small algorithm to ensure there are only two of each color in the matrix so they all have a matching pair. In each iteration of the for loop, I add an event listener to each circle. If the circle is clicked, I want it to change to its color which is stored in color[i].color. However, when I click the circles all it returns is:
Uncaught TypeError: Cannot read property 'elem' of undefined
at main (script.js:39)
at HTMLDivElement.<anonymous> (script.js:31)
Which is referencing:
circle.elem.style.backgroundColor = circle.color;
So I put some console.log() functions in to see what was going on:
if(frequency[num] != 2){
frequency[num]++;
circle[i].color = hue[num];
console.log(circle[i].elem);
console.log(circle[i].color);
circle[i].elem.addEventListener('click', function(){
main(circle[i])
});
}
And this spits out exactly what I expect:
script.js:31 #ff9d00
script.js:30 div data-brackets-id=​"11" class=​"circle" id=​"circle1" /div
script.js:31 #ff9d00
script.js:30 div data-brackets-id=​"12" class=​"circle" id=​"circle2" /div
script.js:31 #0039ff
script.js:30 div data-brackets-id=​"13" class=​"circle" id=​"circle3" /div
script.js:31 #0039ff
So it returns the element reference and the color of the circle. So then I try putting the "circle[i].elem.style.backgroundColor = circle[i].color" into the event listener and I get the same issue as before...
if(frequency[num] != 2){
frequency[num]++;
circle[i].color = hue[num];
console.log(circle[i].elem);
console.log(circle[i].color);
circle[i].elem.addEventListener('click', function(){
circle[i].elem.style.backgroundColor = circle[i].color
});
}
Circles without their colors. The console log statements are on the right-hand side with their specific colors as well...
So I gave up and decided to write that exact line of code outside the event listener to see if that works, and it changed all the circle's colors to their specific color...
if(frequency[num] != 2){
frequency[num]++;
circle[i].color = hue[num];
console.log(circle[i].elem);
console.log(circle[i].color);
circle[i].elem.style.backgroundColor = circle[i].color;
circle[i].elem.addEventListener('click', function(){
circle[i].elem.style.backgroundColor = circle[i].color
});
}
The circles with their specific colors...
There is some problem the event listener not being able to pass the object of a Circle or something... I don't know please help :(
Your problem boils down to the way JS treats var variables - they sort of "leak" into the global scope.
Consider the event listener that you've attached:
circle[i].elem.addEventListener('click', function(){
main(circle[i])
});
So, whenever the listener gets triggered, it calls main() function and passes circle[i] into it. But since i is the variable that's leaked outside of the supposed scope, it always has the value of 16 - the value assigned to it during the last iteration of the for loop. That's why the main() function tries to access a style property of undefined - it's the value of circle[16] that was passed into it.
Here's a couple of ways to fix it:
If you can use ES6 let variables:
Use let i instead of var i in your for loop:
for (let i = 0; i < 16; i++) {
//...
}
If not, a classic way with function closure:
function createListener(j) {
return function () {
main(circle[j])
}
}
// and use it in your 'for' loop later:
circle[i].elem.addEventListener('click', createListener(i));
Here's a useful topic that provides more techniques to avoid this: JavaScript closure inside loops – simple practical example

Can't remove the event Listener, not understanding my mistake

I have a event listener which is assigned to a div using the click listener.
The problem is it does not remove the listener it just keeps adding to it.
This is the script where it is happening:
var resourceCheckout = function (quantity,btn){
//quantity = integer
// btn = document.getElementByID('the_div');
var calculate = function (e) {
var allowed = false;
for(var i in test){
var total = quantity * test[i].q;
if(total > 200){ //if total > 200 remove eventListener
allowed = false; break;
} else {
allowed = true;
}
};
if(allowed){
btn.addEventListener('click',assign,false);
} else {
btn.removeEventListener('click', assign ,false);
}
};
var assign = function (e) {
do_it(quantity); //this gets called more than once when it should be
// a maximum of one
};
calculate();
};
I decided to make a working jsfiddle to show you it in action, simply move the slider then hit the button, it will then call the function the listener is assigned to and count how many times it was called.
JSFIDDLE LINK
I hope someone can explain my mistake as its getting confusing to understand as my script gets more complicated!
The problem is that the function you try to remove has never been added, and so nothing is removed.
Every call to your resourceCheckout function creates a new assign function, which is then used by your calculate function. Since it's a new assign function, it cannot have ever been added to the button, so calling removeEventListener and passing it in has no effect.
If a previous call to resourceCheckout put an assign on a button, you have to use that same function reference to remove it.
This may be clearer with a simpler example:
function foo() {
function bar() {
}
return bar;
}
foo creates a new bar function every time it's called. So:
var b1 = foo();
var b2 = foo();
console.log(b1 === b2); // false
b1 and b2 are different functions.
To make your resourceCheckout work, you'd need to remember the previous assign and use that to remove the handler.
You asked in a comment why this doesn't apply to the slider code in the fiddle, which looks like this (since it's not in the question):
// The OP's slider code
var initSlider = function (el,func,data) {
var clickX = null;
var startSlider = function (e) {
clickX = e.pageX;
document.body.addEventListener('mousemove', calc, false);
document.body.addEventListener('mouseup', endSlider, false);
};
var endSlider = function (e) {
document.body.removeEventListener('mousemove', calc, false);
document.body.removeEventListener('mouseup', endSlider, false);
};
var calc = function (e) {
var dif = e.pageX - clickX;
clickX = e.pageX;
var parentWidth = parseInt(window.getComputedStyle(el.parentNode).width);
var childWidth = parseInt(window.getComputedStyle(el).width);
var childLeft = parseInt(window.getComputedStyle(el).left);
var left = childLeft + dif;
if (left < 0) { left = 0; }
else if (left > (parentWidth-childWidth)) { left = (parentWidth-childWidth); }
el.style.left = left + 'px';
func(data,left,parentWidth-childWidth);
};
el.addEventListener("mousedown", startSlider, false);
};
There, the startSlider code successfully removes a handler from the body element. It works because the startSlider code has a reference to the endSlider function that was created at the same time startSlider was created (the same call to initSlider). So since startSlider has a reference to the same function that was used with addEventListener, removeEventListener works.
This code potentially adds a new listener for the event each time it is called.
Also note that every time you call ResourceCheckout a new different function object is created for variable assign and therefore the removeEventListener will never succeed because that specific function object passed has just been created and was never registered for the event (what was registered was a different function object, created in a previous call).

Creating a loop from a series of onMouseOver Events

How can I create a loop out of this function:
window.onload = function makeHalo() {
document.getElementById("d1").onmouseover = function() {
this.id ="d1On";
this.className="hover";
document.getElementById("menu1").style.color="#6DC5E6";
};
document.getElementById("menu1").onmouseover = function() {
this.style.color="#6DC5E6";
document.getElementById("d1").className="hover";
document.getElementById("d1").id="d1On";
};
document.getElementById("d1").onmouseout = function() {
this.id ="d1";
this.className="";
document.getElementById("menu1").style.color="#FFFFFF";
};
document.getElementById("menu1").onmouseout = function() {
this.style.color="#FFFFFF";
document.getElementById("d1On").className="";
document.getElementById("d1On").id="d1";
};
document.getElementById("d2").onmouseover = function() {
this.id ="d2On";
this.className="hover";
document.getElementById("menu2").style.color="#6DC5E6";
};
document.getElementById("menu2").onmouseover = function() {
this.style.color="#6DC5E6";
document.getElementById("d2").className="hover";
document.getElementById("d2").id="d2On";
};
document.getElementById("d2").onmouseout = function() {
this.id ="d2";
this.className="";
document.getElementById("menu2").style.color="#FFFFFF";
};
document.getElementById("menu2").onmouseout = function() {
this.style.color="#FFFFFF";
document.getElementById("d2On").className="";
document.getElementById("d2On").id="d2";
};
}
The function pretty much learns the ID of an image when its hovered, changes the ID of that element, adds a class to the element, and changes the color of another element
The second part learns the ID of a list item when its hovered, changes its color, and changes the ID of the other image element and adds a class to that element as well.
The onmouseout simply resets everything.
On the actual HTML page, it is a menu page with lists. Below there a continent map, which is a background image. When you hover over a list item, it swaps out a point on a map with another picture for an indicator. You can also hover the points on the map to change the color of the links on the lists.
I tried doing something like this, but the loop only goes to the last iteration for some of the elements. The links change color fine, but it will only swap the picture for "d43" regardless of what link I hover over.
window.onload = function makeHalo() {
var i = 1;
for (i=1; i<44; i++) {
var menu = "menu"+i;
var d = "d"+i;
var On = "d"+i+"On";
document.getElementById(d).onmouseover = function() {
this.id = On;
this.className="hover";
document.getElementById(menu).style.color="#6DC5E6";
};
document.getElementById(menu).onmouseover = function() {
this.style.color="#6DC5E6";
document.getElementById(d).className="hover";
document.getElementById(d).id=On;
};
document.getElementById(d).onmouseout = function() {
this.id = d;
this.className="";
document.getElementById(menu).style.color="#FFFFFF";
};
document.getElementById(menu).onmouseout = function() {
this.style.color="#FFFFFF";
document.getElementById(On).className="";
document.getElementById(On).id=d;
};
}
}
Any help will be greatly appreciated.
The primary technical issue you're facing is that you're creating closures in a loop. Each one of those callbacks closes over the same i variable, whose value will be the same for each of the callbacks (its value after the final iteration). This is fixed by wrapping the body of the loop in its own function that receives i as an argument, thus creating a local copy on each iteration.
There are a number of style and performance issues, as well:
The bodies of those callbacks are in many cases exactly the same (the mouseover and mouseout pairs end up dong the same work in each block).
You're retrieving the same elements by ID repeatedly. This is unnecessary; you should save a reference.
You're identifying the state of an element by changing its ID. This isn't generally how you want to handle this. An ID shouldn't change.
I would write it more like this (addressing the closure issue and the first two bullet items above (not addressing the ID problem)):
for (var i = 1; i <= 2; i++) {
(function(i) {
var d = document.getElementById("d" + i);
var menu = document.getElementById("menu" + i);
d.onmouseover = menu.onmouseover = function() {
d.id = "d" + i + "On";
d.className = "hover";
menu.style.color = "#6DC5E6";
};
d.onmouseout = menu.onmouseout = function() {
d.id = "d" + i;
d.className = "";
menu.style.color = "#FFFFFF";
};
})(i);
}
This handles just two elements; simply change the loop max to make it work for more.
You can see a working demo here:
http://jsfiddle.net/ezYtq/
Is your last div in your HTML "d43" or is it "d44"? Your loop will run through d1 through d43 because you have i<44 which means when i is 44 it will exit the loop so it will stop at d43.
If you want it to get to d44, then either change the condition to: i <= 44
or change it to i < 45
By the way is there is reason you are not using jQuery it's design to make things like this much easier, in several ways. Maybe you listed what you were actually trying to accomplish with this code for example whether it's a menu system or something we might be able to suggest better approaches.
No need for JavaScript here... just use the CSS :hover pseudo-class.
But, to answer your question:
Do not change the id of your element. This seems fundamentally wrong. Change, add, or remove a class instead. What are you trying to accomplish by changing the id?
Don't keep track of id's, just keep track of element references directly.
Most importantly, when you are doing your loop, by the time the functions are called, the value of i is 45, for all elements. Solve this by passing i to a function that creates your event handlers:
window.onload = function makeHalo() {
for (var i = 1; i < 44; i++) {
(function (i) {
var menu = document.getElementById("menu" + i);
var d = document.getElementById("d" + i);
function over () {
d.className = "hover";
menu.style.color = "#6DC5E6";
}
d.onmouseover = over;
menu.onmouseover = over;
function out () {
d.className = "";
menu.style.color = "#FFFFFF";
}
d.onmouseout = out;
menu.onmouseout = out;
})(i);
}
}

Write a wrapper object in Javascript

First off, let me apologize if my question isn't worded correctly - I'm not a professional coder so my terminology might be weird. I hope my code isn't too embarrassing :(
I have a fade() method that fades an image in and out with a mouse rollover. I would like to use a wrapper object (I think this is the correct term), to hold the image element and a few required properties, but I don't know how to accomplish this. fade() is called from the HTML, and is designed to be dropped into a page without much additional setup (so that I can easily add new fading images to any HTML), just like this:
<div id="obj" onmouseover="fade('obj', 1);" onmouseout="fade('obj', 0);">
The fade(obj, flag) method starts a SetInterval that fades the image in, and when the pointer is moved away, the interval is cleared and a new SetInterval is created to fade the image out. In order to save the opacity state, I've added a few properties to the object: obj.opacity, obj.upTimer, and obj.dnTimer.
Everything works okay, but I don't like the idea of adding properties to HTML elements, because it might lead to a future situation where some other method overwrites those properties. Ideally, I think there should be a wrapper object involved, but I don't know how to accomplish this cleanly without adding code to create the object when the page loads. If anyone has any suggestions, I would greatly appreciate it!
Here's my fader method:
var DELTA = 0.05;
function fade(id, flag) {
var element = document.getElementById(id);
var setCmd = "newOpacity('" + id + "', " + flag + ")";
if (!element.upTimer) {
element.upTimer = "";
element.dnTimer = "";
}
if (flag) {
clearInterval(element.dnTimer);
element.upTimer = window.setInterval(setCmd, 10);
} else {
clearInterval(element.upTimer);
element.dnTimer = window.setInterval(setCmd, 10);
}
}
function newOpacity(id, flag) {
var element = document.getElementById(id);
if (!element.opacity) {
element.opacity = 0;
element.modifier = DELTA;
}
if (flag) {
clearInterval(element.dnTimer)
element.opacity += element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity > 100) {
element.opacity = 100;
element.modifier = DELTA;
return;
}
element.opacity = Math.ceil(element.opacity);
} else {
clearInterval(element.upTimer)
element.opacity -= element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity < 0) {
element.opacity = 0;
element.modifier = DELTA;
return;
}
element.opacity =
Math.floor(element.opacity);
}
setStyle(id);
}
function setStyle(id) {
var opacity = document.getElementById(id).opacity;
with (document.getElementById(id)) {
style.opacity = (opacity / 100);
style.MozOpacity = (opacity / 100);
style.KhtmlOpacity = (opacity / 100);
style.filter = "alpha(opacity=" + opacity + ")";
}
}
You are right, adding the handlers in your HTML is not good. You also loose the possible to have several handlers for event attached to one object.
Unfortunately Microsoft goes its own way regarding attaching event handlers. But you should be able to write a small wrapper function to take care of that.
For the details, I suggest you read quirksmode.org - Advanced event registration models.
An example for W3C compatible browsers (which IE is not): Instead of adding your event handler in the HTML, get a reference to the element and call addEventListener:
var obj = document.getElementById('obj');
obj.addEventListener('mouseover', function(event) {
fade(event.currentTarget, 1);
}, false);
obj.addEventListener('mouseout', function(event) {
fade(event.currentTarget, 0);
}, false);
As you can see I'm passing directly a reference to the object, so in you fade method you already have a reference to the object.
You could wrap this in a function that accepts an ID (or reference) and every time you want to attach an event handler to a certain element, you can just pass the ID (or reference) to this function.
If you want to make your code reusable, I suggest to put everything into an object, like this:
var Fader = (function() {
var DELTA = 0.05;
function newOpacity() {}
function setStyle() {}
return {
fade: function(...) {...},
init: function(element) {
var that = this;
element.addEventListener('mouseover', function(event) {
that.fade(event.currentTarget, 1);
}, false);
element.addEventListener('mouseout', function(event) {
that.fade(event.currentTarget, 0);
}, false);
}
};
}())
Using an object to hold your functions reduces pollution of the global namespace.
Then you could call it with:
Fader.init(document.getElementById('obj'));
Explanation of the above code:
We have an immediate function (function(){...}()) which means, the function gets defined and executed (()) in one go. This function returns an object (return {...};, {..} is the object literal notation) which has the properties init and fade. Both properties hold functions that have access to all the variables defined inside the immediate function (they are closures). That means they can access newOpacity and setStyle which are not accessible from the outside. The returned object is assigned to the Fader variable.
This doesn't directly answer your question but you could use the jQuery library. It's simple, all you have to do is add a script tag at the top:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js">
Then your div would look like:
<div id="obj" onmouseover="$('#obj').fadeIn()" onmouseout="$('#obj').fadeOut()">
jQuery will handle all the browser dependencies for you so you don't have to worry about things like differences between firefox and mozilla etc...
If you want to keep your HTML clean, you should consider using JQuery to set up the events.
Your HTML will look like this:-
<div id="obj">
Your JavaScript will look "something" like this:-
$(document).ready(function() {
$("#obj").mouseover(function() {
Page.fade(this, 1);
}).mouseout(function(){
Page.fade(this, 0);
});
});
var Page = new function () {
// private-scoped variable
var DELTA = 0.05;
// public-scoped function
this.fade = function(divObj, flag) {
...
};
// private-scoped function
var newOpacity = function (divObj, flag) {
...
};
// private-scoped function
var setStyle = function (divObj) {
...
};
};
I introduced some scoping concept in your Javascript to ensure you are not going to have function overriding problems.

Categories

Resources