I'm a little confused about prototyping in JS. I've prepared a fiddle for this:
http://jsfiddle.net/GBCav/7/
Markup:
<div class="container1">
<p>Container 1</p>
<button>Turn me (container1) red</button>
</div>
<div class="container2">
<p>Container 2</p>
<button>Turn me (container2) red</button>
</div>
JS:
// Constructor Function
function Box( container ) {
this.container = $(container);
}
// Prototype method
Box.prototype = {
init : function() {
// Assign buttons to object variable
this.button = this.container.find('button');
$this = this; // Set 'this' to Slider object
this.button.on('click', function() {
// It is getting the wrong container here, but why
$this.container.css('background-color','red');
});
}
};
Here's how I call the constructor function:
// Create two instances of box
(function() {
var container1 = new Box( $('div.container1') );
container1.init();
var container2 = new Box( $('div.container2') );
container2.init();
})();
I have two Box-objects created by a constructor function. When I click on a button inside of a box, the background of the CONTAINING box should change the color.
The change of color is handled in the init prototype function of the Box.
However, the wrong box is getting colored with the code above. How do I address the right container?
What am I missing here?
You're missing a var statement:
$this = this;
should be:
var $this = this;
Add in var and it works as expected: http://jsfiddle.net/GBCav/8/
Explanation: When you omit the var keyword, you're assigning $this to a global variable, rather than one limited to the scope of the .init() method. The assignment happens when you call .init(), so calling this method on the second instance re-assigns $this to the second instance, affecting the value of $this in the first instance's event handler as well.
Related
I make a button in HTML, and I want it to call function in javascript:
index.html
<body onLoad="setGameAreaBounds()" onResize="setGameAreaBounds()">
<div id="scoreLabel">Score: 0 </div>
<!--div Group-->
<div>
<p id="pageTitle">Button Chaser</p>
<input type="button" id="startButton" onClick="start()" value="Start"/>
</div>
<!--The following: gameArea and dot is grouped together-->
<div id="gameArea">
<button id="dot" onClick="detectHit()"></button>
</div>
</body>
buttonChaser.js
function detectHit() {
//Increase the var score
}
function setGameAreaBounds() {
//Setting the size of GameBoard/Window
}
function moveDot() {
//randomly move the button(id: dot) within the GameBoard/window
}
function start() {
alert("sometext");
moveDot();
}
The code runs fine if I put moveDot(); function inside setGameAreaBounds();
However, it seems like the button(id: startButton) never connects to function start();
What did I do wrong?
Try moving your functions inside your onLoad function and link them back up to the global scope:
var score = 0;
var aWidth;
var aHeight;
var timer;
var that = this;
function setGameAreaBounds() {
//Setting the size of GameBoard/Window
that.moveDot = function () {
//randomly move the button(id: dot) within the GameBoard/window
}
that.detectHit = function() {
//Increase the var score
}
that.start = _start; // link desired name to reference to existing function
}
function() _start {
alert("sometext");
moveDot();
}
Basically, your html functions are accessing functions defined in the global scope after the dom elements were created. So no reference to the function exists when the dom is created. The setGameAreaBounds function gets called after the dom is ready - onLoad. JavaScript functions each have their own scope so you need to pass this from the parent using an unique reference. Then you can assign the names you want to the functions.
A better approach would be to define all scripts after
Many programs use an onReady function that waits until the dom is loaded before defining any javascript functions. This is a good approach.
I have html buttons "classA","classB" and "classC" to which I assign the onclick handler function as follows..
var classA = document.getElementById('classA');
classA.onclick = filterByClassA;
var classB = document.getElementById('classB');
classB.onclick = filterByClassB;
var classC = document.getElementById('classC');
classC.onclick = filterByClassC;
These 3 functions do the same thing, only difference being the class.
So, can I have a single function assigned to these buttons, called with different parameters for each button. Something like below
var classA = document.getElementById('classA');
classA.onclick = filterByClass('classA');
var classB = document.getElementById('classB');
classB.onclick = filterByClass('classB');
var classC = document.getElementById('classC');
classC.onclick = filterByClass('classC');
I know this is a function call and not assignment and this is wrong but is there a way I can achieve this i.e. assign a function and pass parameter at the same time but not call it?
function filterByClass(className)
{
return function()
{
// Do something with className
console.log(className);
}
}
Bind can help you out here: Its called partial application.
Bind Syntax
fun.bind(thisArg[, arg1[, arg2[, ...]]])
1st param is scope of the function when it is called.
From 2nd you can pass any number of agruments. See the below code to know how it works.
Code:
var classA = document.getElementById('classA');
classA.onclick = filterByClass.bind(classA, 'classA');
var classB = document.getElementById('classB');
classB.onclick = filterByClass.bind(classB, 'classB');
var classC = document.getElementById('classC');
classC.onclick = filterByClass.bind(classC, 'classC');
function filterByClass(className, eventObject) {
console.log(this, className, eventObject);
}
Update:
Check out the Compatibility section in the above MDN link. You may need to use it, if you are going to use bind in non modern browsers.
i always try to keep the code as short as possible
so if your buttons are inside a container you can do that.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>atest</title>
<script>
window.onload=function(){
var c=document.getElementsByTagName('div')[0].childNodes,
l=c.length,
filterByClass=function(){
console.log(this.id);//this.id is the classA or whatever
};
while(l--){
c[l].onclick=filterByClass;
}
}
</script>
</head>
<body>
<div>
<button id="classA">A</button>
<button id="classB">B</button>
<button id="classC">C</button>
</div>
</body>
</html>
in this case
document.getElementsByTagName('div')[0]
returns the first div in the document
childNodes
give u the list of the buttons inside that div
the while function adds the onclick event with your function 'filterByClass'
inside filterByClass u can access the element by this and so return it's id with this.id
I have several fish images, within links, which are listed like so...
<img src="img/fish1.png" id="fish1"/>
<img src="img/fish2.png" id="fish2"/>
I want to take id 'fish1', 'fish2', ect. (based on which is clicked), and use a variable to replace some text.
The name of the variables are...
var fish1Hunger
var fish2Hunger
And the function I want to call with a variable is...
$(function() {
$('a.trigger').click(function() {
$('#popUp h3').html(REPLACE WITH VARIABLE ASSOCIATED WITH THE FISH);
});
});
How can I call the variable associated with the IMG id?
window[e.srcElement.id+"Hunger"];
Put that inside your click event with e being the event (function (e){...) and it should access the variable as long as it is in the global scope.
jsBin demo
var fish1Hunger = "Fish 1 is hunger!! Feed him!"
var fish2Hunger = "Fish 2 is hunger!! Feed him!"
$('a.trigger').click(function (e) {
e.preventDefault();
var ImLookingFor = $(this).find('img').attr('id')+'Hunger';
$('#popUp h3').html(eval(ImLookingFor));
});
I'm working on a simple client side interface where I have a jQuery object that I want to access directly when clicking on a hyperlink. Simplified code:
<div class="controls">
<div class="score">
<a class="button" href="/add">Add points!</a>
</div>
</div>
$(".controls").myControls();
$.fn.myControls = function() {
return $.extend(this, $.myControls).initialize();
}
$.myControls = {
initialize: function() {
this.scoreElement = $("div.score", this);
this.linkElement = $("a", this.scoreElement);
this.scoreElement.score = 0;
var _this = this;
this.linkElement.click(function() {
_this.clickHandler(this);
});
},
clickHandler: function(element) {
var scoreElement = $(element).parent();
scoreElement.score = 1;
}
}
Explanation: .controls element has .score element which doubles as a container for score information (this.scoreElement.score). When I click on a link within the .score element, I find the parent element, which is the same element in the DOM as this.scoreElement and try to set its score property to 1. Obviously, this won't work, as the local scoreElement.score property in the clickHandler method is "undefined".
So here's my question: is there a simple way to access my this.scoreElement object directly through traversing the DOM with jQuery?
Surely I can check if this.scoreElement == $(element).parent() in some way and then access the right property in my this.scoreElement object, but direct access would be more elegant and robust. Is this possible? Am I going at it the wrong way? Thanks!
PS: Ignore the fact I use parent() to find the scoreElement, I only use it to illustrate my problem. Unless it is part of the problem, in that case don't ignore :)
While it's certainly possible to use your own 'control-object' to store the related data, I usually prefer to rely on jQuery doing it - with .data() method, like this:
$(this.scoreElement).data('score', 0); // in initialize()
$(this).parent().data('score', 1); // in clickHandler()
This approach allows me to scale more easily, as I never have to fear 'overlapping' issues, using a single 'control' object rather than object-for-element.
I would think that if you used jQuery's proxy function for your click handler, you then could just go this.scoreElement inside of clickHandler and you wouldn't even need to traverse the DOM. Like this:
$.myControls = {
initialize: function() {
this.scoreElement = $("div.score", this);
this.linkElement = $("a", this.scoreElement);
this.scoreElement.score = 0;
this.linkElement.click($.proxy(this.clickHandler, this));
},
clickHandler: function(event) {
var element = event.target;
this.scoreElement.score = 1;
}
}
After progressive simplification (and storing the score slightly differently) I get the code below, in which scoreElement is discovered once per .controls div, then held in a closure to make it available to its corresponding click handler. You could alternatively use .closest() - see commented out line.
$.fn.myControls = function() {
return this.each(function() {
var scoreElement = $("div.score", $(this));
scoreElement.data('score', 0);
$("a", scoreElement).on('click', function() {
scoreElement.data('score', 1);//get scoreElement from closure formed by the outer "each" function.
//$(this).closest(".score").data('score', 1);//alternative to the line above, not requiring closure.
});
});
};
Call as in the question with:
$(".controls").myControls();
This is so trivial and unidimensional it doesn't really warrant, in its own right, a jQuery plugin. Unless there was some compelling reason for a plugin (eg. reuse or the need for closely related methods), then I would phrase it as follows :
$(".controls").each(function() {
var scoreElement = $("div.score", $(this));
scoreElement.data('score', 0);
$("a", scoreElement).on('click', function() {
scoreElement.data('score', 1);//get scoreElement from closure formed by the outer "each" function.
//$(this).closest(".score").data('score', 1);//alternative to line above, not requiring closure.
});
});
That's the same code with the plugin wrapper removed and attached directly to the same base jQuery object.
And if you really wanted, you could write the whole thing in three lines as follows:
$(".controls").find("div.score").data('score', 0).find("a.button").on('click', function() {
$(this).closest(".score").data('score', 1);
});
I want to set timeouts dynamically in jQuery. The dynamically set timeout functions need to use $("this"), but I can't seem to get it working.
An exmple:
$("div").each(function(){
var content = $(this).attr('data-content')
setTimeout("$(this).html('"+content+"')",$(this).attr('data-delay'));
});
http://jsfiddle.net/qmhmQ/
What is the best way to do this?
$("div").each(function(){
var content = $(this).attr('data-content'),
$this = $(this); // here $this keeps the reference of $(this)
setTimeout(function() {
// within this funciton you can't get the $(this) because
// $(this) resides within in the scope of .each() function i.e $(this)
// is an asset of .each() not setTimeout()
// so to get $(this) here, we store it within a variable (here: $this)
// and then using it
$this.html(content);
}, $this.attr('data-delay'));
});
DEMO
Your code should look like this:
pass a function instead of a string.
Explanation:
When passing a string to setTimeout you get problems, because it runs in a different scope than you original one, and thus you get errors.
use the jQuery data()method
$("div").each(function(){
var content = $(this).attr('data-content'),
$el = $(this),
setContent = function(){
$el.html(content);
}
setTimeout(setContent,$el.data('delay'));
});
You can assign a function to a variable and pass that variable as parameter to setTimeout, this is the cleanest way.
Use closures (some tutorials).
Using strings with setTimeout is not a good idea. Also beware this, since it can change its context (ie. call-site) if used inside a closure.
If using data attributes you can use the jQuery data function.
$("div").each(function() {
var instance = $(this);
var content = instance.data('content');
var method = function() {
instance.html(content);
};
setTimeout(method, instance.data('delay'));
});
div {
border: 1px solid black;
margin: 5px;
height: 1.5em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-content="fus" data-delay="1000"></div>
<div data-content="ro" data-delay="2000"></div>
<div data-content="dah" data-delay="5000"></div>
I am just expanding answer one above,
Use class or id to refer div in JavaScript. This will avoid further tag name conflicts in the page.
So your updated HTML is,
<div data-content="fus" data-delay="1000" class="dv"></div>
<div data-content="ro" data-delay="2000" class="dv"></div>
<div data-content="dah" data-delay="5000" class="dv"></div>
Now your updated JavaScript code is,
$(".dv").each(function(){
var content = $(this).attr('data-content'),
$this = $(this);
setTimeout(function() {
$this.html(content);
}, $this.attr('data-delay'));
});
Where main line is
$this = $(this);
Where we are assigning the current element to our variable used in the setTimeout function.
Please refer this link
Take $(this) out of settimeout and save that in local variable say 'self' just after $("div").each(function(){ this line
var self=$(this);
and use that self further.
The following seems like a good compromise of whitespace, readability and revealing intention.
$('div').each(function(){
var element = $(this)
var content = element.attr('data-content')
var delayms = element.attr('data-delay')
var action = function() { element.html(content) }
setTimeout(action, delayms)
})
SEE: http://jsfiddle.net/wilmoore/LSs6g/