Using Function Argument within a Variable Reference - javascript

I know this is very naive, but I'm trying to do something like this:
var add01in = "#fff";
var add01out = #000";
function over(id) {
var dupe = id.attr('id')
id.style.backgroundColor = (dupe + 'in');
}
function out(id) {
var dupe = id.att('id');
id.style.backgroundColor = (dupe + 'out');
}
<div id="add01" onmouseover="over(this)" onmouseout="out(this)">Hello World!</div>
So I want it, when the user mouseovers the div with ID = "add01", for the color to change to whatever the value of the variable "add01in" is. And when they mouseout it changes to the value of "add01out".
The only thing is I cannot for the life of me figure out how to get the words 'in' and 'out' to add on to the end of the argument's ID.
So for instance, onmouseover should make id.style.backgroundColor = add01in (as in the var) and then equal to the var's value, so #fff
sorry for making this so cryptic. any help is welcome... or alternatives, but at this time I can't find any way around this since I need to use the function a LOT of time, and with changing colours, etc.

You may use an object for the colors of the ids and their state. And use the parameter of over/out for the id.
var color = {
add01in: "#fff",
add01out: '#000'
};
function over(id) {
document.getElementById(id).style.backgroundColor = color[id + 'in'];
}
function out(id) {
document.getElementById(id).style.backgroundColor = color[id + 'out'];
}
<div id="add01" onmouseover="over('add01')" onmouseout="out('add01')">Hello World!</div>

First, att is not valid. Maybe .getAttribute? Second, you are trying to reference a variable with a variable name, which is not something you can do with JS. Use an object instead:
var colors = {
add01in: '#fff',
add01out: '#000'
}
This way you can reference them with
id.style.background = colors[dupe + 'in'];
HOWEVER you should really, really use CSS for this kind of thing.

If you want to change the text color:
var t = document.getElementById('add01');
var overColor = '#585858';
var outColor = '#000';
t.addEventListener('mouseover', function (event) {
t.style.color = overColor;
});
t.addEventListener('mouseout', function (event) {
t.style.color = outColor;
});
if you want to change the bg color
var t = document.getElementById('add01');
var overColor = '#585858';
var outColor = '#000';
t.addEventListener('mouseover', function (event) {
t.style.background = overColor;
});
t.addEventListener('mouseout', function (event) {
t.style.background = outColor;
});
Don't think you need to interpolate using the ID yeah? I think your hangup here is not realizing you can save a reference (in my example it's variable t) to the element you want to manipulate. I think that's why you were essentially trying to simulate some kind of introspection using the ID to lookup the var name.
Regarding CSS-- you could use it, but then you would have to make sure all the colors you want have classes in the stylesheet. If you're looking for more dynamic control than I can see why you'd want to do it this way. If the colors never change, then make a CSS class:
.mouse-over { color: #fff; }
and just add/remove it in the event handlers with the classList API which you can read about on MDN.

Related

Simple inquiry on a very small jQuery plugin

Here's a fairly newbish question: Testing out a simple function where it fades out on hover and fades back in on mouseout. Was playing around with parameters so please excuse how this doesn't make a whole lot of sense, but I just wanted to use 'opacity' in the parameter instead of hardcoding it (again, just to understand things). Here is my code:
$.fn.hoverImage = function(property) {
//add event handler for each image (otherwise it will add the same event handler for all images all at once)
this.each(function() {
var img = $(this);
//hover: first function is for mouseover, second is on mouseout
img.hover(function() {
img.animate({
property:0
}, 200);
}, function() {
img.animate({
property:1
}, 200);
});
});
}
I call it doing:
$('.project-img').hoverImage('opacity');
If I write opacity as a string in the parameter it doesn't work, and if I don't put it in a string I get this error:
Uncaught ReferenceError: opacity is not defined
Just want to know why. It does work if I use the word opacity in the plugin, just to clarify that. Thanks.
The code
{property: 0}
creates an object with a property named property with a value of 0. In particular, it does not look up a variable named property and use that as the property name. If you wanted to do that, you'd have to break it out:
var obj = {};
obj[property] = 0;
/* use obj */
Compare this to using obj.property, which, too, would not use the property variable.
You need to use bracket notation as the member operator, because your property key is stored in a variable
$.fn.hoverImage = function (property) {
//add event handler for each image (otherwise it will add the same event handler for all images all at once)
this.each(function () {
var img = $(this);
//hover: first function is for mouseover, second is on mouseout
img.hover(function () {
var opts = {};
opts[property] = 0;
img.animate(opts, 200);
}, function () {
var opts = {};
opts[property] = 1;
img.animate(opts, 200);
});
});
}
Demo: Fiddle

JavaScript access elements from custom object

This must be a very stupid question, but I just can't get it to work.
I'm creating my own UIKit for iOS. (Website-kit which will allow iPhone-like interfaces).
But, I'm trying to create a JavaScript library, which can be used to change several elements of the document. For instance, set a custom background colour when the document loads.
I'm trying to do that with object-orientated JavaScript. Like this:
var UI = new Interface();
UI.setBackground("#000");
How could I achieve this?
(So the custom "UI" Object, and (an example) on how to change the background color of the document, from INSIDE the object.)
You can save a reference to the DOM inside the JS object and rewrite it as needed.
function Interface() {
this.setBackground = function (color) {
this.pointTo.style.background = color;
};
this.pointTo = document.body;
}
You can initialize this by:
var UI = new Interface();
UI.pointTo = document.getElementById('some_id');
UI.setBackground("#000");
// Set another style, on a different element
UI.pointTo = document.getElementById('some_other_id');
UI.setBackground("#FFF");
This is a simple implementation and need to be allot smarter, but it should do the job.
Edit:
Made a mistake in original posting, and fixed erroneous code. Also made an example: http://jsfiddle.net/HpW3E/
Like silverstrike's code, but you can pass the target object in the interface constructor to don't get trouble in the future.
function Interface(target) {
target = target || document.body;
this.setBackground = function (color) {
target.style.background = color || 'white';
};
}
Ok now you can do this:
var UI = new Interface(document.body);
UI.setBackground("#000");
or even in somecases that you are applying the interface in global scope !ONLY!:
var UI = new Interface(this.body);
UI.setBackground("#000");
Also will work as this:
var UI = new Interface();
UI.setBackground("#000");
Here is a very simple approach
// define the object
var Interface = function () {
var interface = document.getElementById("interface"); // just an example
// your new methods
this.setBackground = function (color) {
interface.style.backgroundColor = color;
}
// rest of your code
}
now you can make use of it
var UI = new Interface();
UI.setBackground("#000");

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);
}
}

Adding an eventListener to a div that can refer to the object that created the div

I have an object that counts chars in an input and displays the number of chars in a div next to the input. I want to add a click event listener to the div that when clicked invokes a function in the object that created the div. I'm confusing myself now so here is some code:
var CharDisplay = function(input, max) {
this.input = input;
this.maxChars = max;
this.direction = "countUp";
this.div = document.createElement("div");
this.div.setAttribute("class", "charCounter");
this.input.parentNode.appendChild(this.div);
var that = this; // I don't like doing this
// This function toggles the direction property and tells the extension
// to update localStorage with the new state of this char counter
var toggleDirection = function() {
that.direction = that.direction === "countUp" ? "countDown" : "countUp";
that.update();
chrome.extension.sendRequest({
"method" : "updateCharCounter",
"id" : that.input.id,
"state" : {
"direction" : that.direction,
"limit" : that.maxChars
}
});
}
this.div.addEventListener("click", toggleDirection);
}
The behaviour I want is that when the div is clicked the Char Counter switches between counting down ('15 chars left') and counting up ('30 of 45 chars'). I store the state of the char counter in localStorage so that whatever state the user left the char counter in they will find it that way when they come back to it.
Now this code actually works fine but I can't escape the feeling that there is a more elegant way of doing it. I had to add the var that = this to get it to work but I always feel like that is a 'hack' and I try to avoid it if I can.
Can you think of a better way of achieving this or should I stop trying to fix what isn't broke?
The var that = this trick is quite common in JavaScript.
The alternative is to bind the function to a given context:
function bind(context, fun) {
return function() {
fun.call(context, arguments);
};
}
This function returns a function that, when called, will call fun with the given context.
You can use it like this:
this.div.addEventListener("click", bind(this, toggleDirection));
With this, toggleDirection will be called with this as context (this in toggleDirection will be the same as the one at the time you called addEventListener).
See also Function.bind.

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