Javascript scope of object property - javascript

Let's say I want to make a Javascript class for boxes in my page. When an object of that class is made, I want to add a click event to it that would require me accessing one or many of its unique properties. How would I do that? Here's an example:
function Box(boxClassName, originalColor, changeColor){
this.boxClassName = boxClassName;
this.originalColor = originalColor;
this.changeColor = changeColor;
this.initialize = function (){
var html = '<div class="' + this.boxClassName + '></div>';
$(document).append(html);
$(this.boxClassName).css("background-color", originalColor);
// Toggle box color on click
$(this.boxClassName).click(function (){
// this.boxClassName is undefined here, none of this works
if ($("." + this.boxClassName).css("background-color") == originalColor) {
$("." + this.boxClassName).css("background-color", this.changeColor);
} else {
$("." + this.boxClassName).css("background-color", this.originalColor);
}
});
};
this.initialize();
}

There are two ways:
1) Keep a reference to this in a variable and use that:
// Toggle box color on click
var self = this;
$(this.boxClassName).click(function () {
if ($("." + self.boxClassName).css("background-color") == originalColor) {
$("." + self.boxClassName).css("background-color", self.changeColor);
} else {
$("." + self.boxClassName).css("background-color", self.originalColor);
}
});
2) Or bind the click callback to the Box
$(this.boxClassName).click(function () {
if ($("." + this.boxClassName).css("background-color") == originalColor) {
$("." + this.boxClassName).css("background-color", this.changeColor);
} else {
$("." + this.boxClassName).css("background-color", this.originalColor);
}
}.bind(this));

You cannot to access property of Box in onclick event. If you would like to store data of html element, you have to use http://api.jquery.com/jquery.data/. By the way, below code might work for you purpose.
if ($(this).css("background-color") == originalColor) {
$(this).css("background-color", this.changeColor);
} else {
$(this).css("background-color", this.originalColor);
}

First of all, you can bind function to get proper this
$(this.boxClassName).click(function (){
// this.boxClassName is now defined here
if ($("." + this.boxClassName).css("background-color") == originalColor) {
$("." + this.boxClassName).css("background-color", this.changeColor);
} else {
$("." + this.boxClassName).css("background-color", this.originalColor);
}
}.bind(this));
Also consider using slightly different approach
function Box(boxClassName, originalColor, changeColor) {
var changedBg = false, div = $('<div />'), handler = function ( e ) {
$(this).css('background-color', changedBg ? originalColor : changeColor)
changedBg = !changedBg
}
div.addClass(boxClassName)on('click', handler).appendTo(document)
}

Related

Jquery : swap two value and change style

i need to make a script for select a black div by click(go red), and put black div value into a white div value by another click, this is ok but when i try to swap values of two white case, the change do correctly one time, but if i retry to swap two value of white case the values swap correctly but whitout the background color red.
This is my code :
var lastClicked = '';
var lastClicked2 = '';
$(".blackcase").click(function(e) {
var i = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.blackcase').removeClass('red');
if (lastClicked != this.id) {
$(this).addClass('red');
var currentId = $(this).attr('id');
var currentVal = $(this).html();
$(".whitecase").click(function(e) {
$('.blackcase').removeClass('red');
var currentId2 = $(this).attr('id');
if (i <= 0 && $("#" + currentId2).html().length == 0) {
$("#" + currentId2).html(currentVal);
$("#" + currentId).html("");
i = 1;
}
});
} else {
lastClicked = this.id;
}
}
});
$(".whitecase").click(function(e) {
var j = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.whitecase').removeClass('red');
if (lastClicked2 != this.id) {
$(this).addClass('red');
var currentId0 = $(this).attr('id');
var currentVal0 = $(this).html();
$(".whitecase").click(function(e) {
e.preventDefault();
var currentId02 = $(this).attr('id');
var currentVal02 = $(this).html();
if (j <= 0 && currentVal0 != currentVal02) {
$('.whitecase').removeClass('red');
$("#" + currentId02).html(currentVal0);
$("#" + currentId0).html(currentVal02);
j = 1;
return false;
}
});
} else {
lastClicked2 = this.id;
}
}
});
This is JSfiddle :
https://jsfiddle.net/12gwq95u/12/
Try to take 12 and put into first white case, put 39 into second white case, click on the white case with 12 (go red) then click on the white case with 39, the values swap correctly with the red color when it's select, but if you try to reswap two whitecase values thats work but without the red color.
Thanks a lot
I have spent some time to rewrite your code to make it more clear. I don't know what exactly your code should do but according to the information you have already provided, my version of your code is the following:
var selectedCase = {color: "", id: ""};
function removeSelectionWithRed() {
$('div').removeClass('red');
}
function selectWithRed(element) {
removeSelectionWithRed();
element.addClass('red');
}
function updateSelectedCase(color, id) {
selectedCase.color = color;
selectedCase.id = id;
}
function moveValueFromTo(elemFrom, elemTo) {
elemTo.html(elemFrom.html());
setValueToElem("", elemFrom);
}
function setValueToElem(value, elem) {
elem.html(value);
}
function swapValuesFromTo(elemFrom, elemTo) {
var fromValue = elemFrom.html();
var toValue = elemTo.html();
setValueToElem(fromValue, elemTo);
setValueToElem(toValue, elemFrom);
}
function isSelected(color) {
return selectedCase.color == color;
}
function clearSelectedCase() {
selectedCase.color = "";
selectedCase.id = "";
}
function elemIsEmpty(elem) {
return elem.html().length == 0;
}
$(".blackcase").click(function (e) {
if (elemIsEmpty($(this))) {
return;
}
alert("black is selected");
selectWithRed($(this));
updateSelectedCase("black", $(this).attr("id"), $(this).html());
});
$(".whitecase").click(function (e) {
removeSelectionWithRed();
if (isSelected("black")) {
alert("moving black to white");
moveValueFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
if(isSelected("white") && selectedCase.id !== $(this).attr("id")) {
alert("swap whitecase values");
swapValuesFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
alert("white is selected");
selectWithRed($(this));
updateSelectedCase("white", $(this).attr("id"), $(this).html());
});
Link to jsfiddle: https://jsfiddle.net/12gwq95u/21/
If my answers were helpful, please up them.
It happens because you have multiple $(".whitecase").click() handlers and they don't override each other but instead they all execute in the order in which they were bound.
I advise you to debug your code in browser console by setting breakpoints in every click() event you have (in browser console you can find your file by navigating to the Sources tab and then (index) file in the first folder in fiddle.jshell.net).
In general I think you should rewrite you code in such a way that you won't have multiple handlers to the same events and you can be absolutely sure what your code does.

Why can I not select an element loaded with $.get in JQuery [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I have a series of buttons loaded from a JSON, which in turn should disappear and append other buttons on click.
Like so (the first level of buttons is already on the page and reacting properly to other events like hover):
...
$(document).on('click', "#subcategoryButtons button", function () {
getTemplate('imgMenu.html');
var entryIndex = this.id[0];
var subentryIndex;
if (this.id[1] === '0')
{
subentryIndex = this.id.slice(-1);
}
else
{
subentryIndex = this.id.slice(-2);
}
var imgs = data.category[entryIndex].subcategory[subentryIndex].imgs;
$.each(imgs, function (imgIndex)
{
var imgName = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgName;
var imgId = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgId;
$('#imgButtons span').append('<button id="' + imgId + '">' + imgName + '</button>');
});
});
}
;
...
This is the content of the template being loaded:
<div id="imgSpace">
<aside id="overlayRight">
Right Overlay space
</aside>
<div id="overlayBottom">
Bottom Overlay
</div>
</div>
<nav id="imgButtons" class="resizable">
<span></span>
</nav>
Here's the getTemplate code:
function getTemplate(templateUrl)
{
$.get('templates/' + templateUrl, function (content)
{
if (templateUrl === 'leftMenu.html')
{
$('#leftMenu').html(content);
}
else
{
$('#main').html(content);
}
});
}
Even though it should append the buttons to the #imgButtons span, it seems as if it cannot select any of the elements in the template just loaded. If I try to append the buttons to another part of the page (say like the left menu, which is not recently loaded) or instead of getting a template I simply clear out the HTML in the main, the attachment works. So it appears that the issue is how to select elements that have been loaded. Any help would be greatly appreciated.
Thanks to everyone who pointed me in the right direction. In the end I didn't use Ajax but deferred.done like so:
$(document).on('click', "#subcategoryButtons button", function () {
var entryIndex = this.id[0];
var subentryIndex;
if (this.id[1] === '0') {
subentryIndex = this.id.slice(-1);
} else {
subentryIndex = this.id.slice(-2);
}
var imgs = data.category[entryIndex].subcategory[subentryIndex].imgs;
$('main').html('');
$.get("templates/imgMenu.html", function (content)
{
$('#main').html(content);
}).done(function () {
$.each(imgs, function (imgIndex) {
var imgName = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgName;
var imgId = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgId;
console.log(entryIndex);
$('#imgButtons span').append('<button id="' + imgId + '">' + imgName + '</button>');
});
});
});
}
;
You're using the wrong selector #imgButtons button should be #imgButtons span to select the span in #imgButtons
Also your template is loaded asynchronously so you'll have to wait until it is loaded (via a callback function) to manipulate it. something like
$(document).on('click', "#subcategoryButtons button", function () {
getTemplate('imgMenu.html', callback);
function callback(){
var entryIndex = this.id[0];
var subentryIndex;
if (this.id[1] === '0')
{
subentryIndex = this.id.slice(-1);
}
else
{
subentryIndex = this.id.slice(-2);
}
var imgs = data.category[entryIndex].subcategory[subentryIndex].imgs;
$.each(imgs, function (imgIndex)
{
var imgName = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgName;
var imgId = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgId;
$('#imgButtons span').append('<button id="' + imgId + '">' + imgName + '</button>');
});
}
});
...
function getTemplate(templateUrl, callback)
{
$.get('templates/' + templateUrl, function (content)
{
if (templateUrl === 'leftMenu.html')
{
$('#leftMenu').html(content);
}
else
{
$('#main').html(content);
}
callback();
});
}
let's make a bit modification on your code, use promises - the jqxhr object returned by the ajax method implements the promise interface, therefore you could make use of it.
function getTemplate(templateUrl)
{
return $.get('templates/' + templateUrl, function (content)
{
if (templateUrl === 'leftMenu.html')
{
$('#leftMenu').html(content);
}
else
{
$('#main').html(content);
}
});
}
use it in this way
$(document).on('click', "#subcategoryButtons button", function (e) {
getTemplate('imgMenu.html').then(function () {
var entryIndex = this.id[0];
var subentryIndex;
if (this.id[1] === '0') {
subentryIndex = this.id.slice(-1);
} else {
subentryIndex = this.id.slice(-2);
}
var imgs = data.category[entryIndex].subcategory[subentryIndex].imgs;
$.each(imgs, function (imgIndex) {
var imgName = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgName;
var imgId = data.category[entryIndex].subcategory[subentryIndex].imgs[imgIndex].imgId;
$('#imgButtons span').append('<button id="' + imgId + '">' + imgName + '</button>');
});
});
});

What about variable scopes inside a callback function

If I have a callback function within a callback function I can't reach the variables declared inside the first callback.
See example (Uses some jquery):
my fiddle
Why is this?
Or
What am I doing wrong!?
I am using this in the following:
$('<div/>').html(data).find('load').each(function(id, deze) {
var method = $(deze).attr("method");
var animate = $(deze).attr("animate");
var locatie = $(deze).attr("id");
var html = $(deze).html();
if(!method){
method = 'overide';
}
if(!locatie){
error('A002','No loadloc');
}
if(!html){
html = '';
}
if(!animate){
animate = false;
}
else if(animate == 'fade'){
var effectout = 'fade';
var timeout = 100;
var optionsout = {
easing: 'swing'
};
var effectin = 'fade';
var timein = 600;
var optionsin = {
easing: 'swing'
};
}
else{
animate = false;
error('A006','Animation: "' + animate + '" does not exist');
}
//here I make callback or direct function call
if(animate){
$('#' + locatie).hide(effectout, optionsout, timeout, load());
}
else{
load();
}
function load(){
console.log('hallo');
console.log(html);
if(method == 'overide'){
$('#' + locatie).html(html);
}
else if(method == 'tablerow'){
var html = $('tr', deze).html();
$('#' + locatie).html(html);
}
else if(method == 'append'){
$('#' + locatie).append(html);
}
else if(method == 'prepend'){
$('#' + locatie).prepend(html);
}
else{
error('A007','load method: "' + method + '" does not exist');
}
if(animate){
$('#' + locatie).show(effectin, optionsin, timein);
}
}
});
Why is this?
It is not. You can simply access something, it is in the scope of the load function that you posted.
What am I doing wrong!?
You're not passing the load function itself as the callback, but it's result - you're immediately calling it (without arguments). You want
$('#' + locatie).hide(effectout, optionsout, timeout, load);
// ^^
//It seems I can't reach html and other variables here
console.log(this.html);
That's not a variable, that's a property of this (and the this keyword points to different things in the different calls). What you want is a simple variable:
console.log(html);
Same story for this.locatie and the others.
I changed your code a bit and I believe this is what you were trying to do:
http://jsfiddle.net/smerny/2M6k3/
var anothercallbackfunctiontriggerthing = function(cb) {
cb();
}
$('.something').each(function(id, value) {
var something = 'something';
var load = function(){
// can't reach something here
console.log(something);
}
anothercallbackfunctiontriggerthing(load);
});
As load() didn't return anything, I assume you were wanting to pass it as a callback (load) and then call it from within anothercallbackfunctiontriggerthing. The above code will print "something" for each .something.
The reason it doesn't work is because the keyword this changes meanings within the context of load() since you're defining a function using function load(). There are several ways of getting around this; here's one:
Before:
if(animate){
$('#' + locatie).hide(effectout, optionsout, timeout, load());
}
else{
load();
}
use
var load = (function(el) {
return function() {
console.log('hallo');
//It seems I can't reach html and other variables here
console.log(el.html);
if(method == 'overide'){
$('#' + el.locatie).html(el.html);
}
else if(method == 'tablerow'){
var html = $('tr', deze).html();
$('#' + el.locatie).html(el.html);
}
else if(method == 'append'){
$('#' + el.locatie).append(el.html);
}
else if(method == 'prepend'){
$('#' + el.locatie).prepend(el.html);
}
else{
error('A007','load method: "' + el.method + '" does not exist');
}
if(el.animate){
$('#' + el.locatie).show(el.effectin, el.optionsin, el.timein);
}
};
})(this);
What I did there was to change all references to this with el. And then I injected this as el by using the following format:
load = ( function(el) { return yourFunction() {...}; })(this);
Let me know if that makes sense and if it works for you :)

Initialize onchange event inside a loop - Javascript

I'm trying to bind an onChange event to 7 dropdowns inside a loop. But when any of dropdown changes, the onChange event for the last one is always executed.
$(function () {
for (var i = 1; i <= 7; i++) {
$('select[id$="bodysys' + i + '"]').change(function () {
if (this.value == "99")
enabletextbox($('input[id$="bodysys' + i + 'spec"]')[0]);
});
}
}
How to make onChange work for all elements separately?
This is called the last one only problem, and is solved using a closure:
$(function () {
for (var i = 1; i <= 7; i++) {
(function (i) {
$('select[id$="bodysys' + i + '"]').change(function () {
if (this.value == "99")
enabletextbox($('input[id$="bodysys' + i + 'spec"]')[0]);
});
})(i);
}
}
It creates a new scope, so when i is changed in the original scope, it will not be changed in the old scope.
$('select[id*="bodysys"]').each(function (index) {
$(this).change(function () {
if ($("option:selected", this).val() == "99")
enabletext($('input[id$="bodysys' + (index + 1) + 'spec"]')[0]);
});
});
This way works too.

How can I re enable a setInterval after I called clearInterval?

Here's my javascript:
<script type="text/javascript">
var timer;
$(document).ready(function () {
timer = setInterval(updatetimerdisplay, 1000);
$('.countdown').change(function () {
timer = setInterval(updatetimerdisplay, 1000);
});
function updatetimerdisplay() {
$(".auctiondiv .auctiondivleftcontainer .countdown").each(function () {
var newValue = parseInt($(this).text(), 10) - 1;
$(this).text(newValue);
if (newValue >= 9) {
$(this).css("color", "");
$(this).css("color", "#4682b4");
}
if (newValue == 8) {
$(this).css("color", "");
$(this).css("color", "#f3982e");
}
if (newValue == 5) {
$(this).css("color", "");
$(this).css("color", "Red");
}
if (newValue <= 1) {
//$(this).parent().fadeOut();
clearInterval(timer);
}
});
}
});
var updateauctionstimer = setInterval(function () {
$("#refreshauctionlink").click();
}, 2000);
function updateauctions(response) {
var data = $.parseJSON(response);
$(data).each(function () {
var divId = "#" + this.i;
if ($(divId + " .auctiondivrightcontainer .latestbidder").text() != this.b) {
$(divId + " .auctiondivrightcontainer .latestbidder").fadeOut().fadeIn();
$(divId + " .auctiondivrightcontainer .auctionprice .actualauctionprice").fadeOut().fadeIn();
$(divId + " .auctiondivleftcontainer .countdown").fadeOut().fadeIn();
}
$(divId + " .auctiondivrightcontainer .latestbidder").html(this.b);
$(divId + " .auctiondivrightcontainer .auctionprice .actualauctionprice").html(this.p);
if ($(divId + " .auctiondivleftcontainer .countdown").text() < this.t) {
$(divId + " .auctiondivleftcontainer .countdown").html(this.t);
}
});
}
</script>
Basically, I want to turn the timer back on, if any .countdown element has it's text change.
The text will change because of an AJAX call I use to update that value.
Currently the timer doesn't re enable and the countdown freezes after the value of .Countdown is changed. I think that the change() event fires when the text of an element changes. Is this correct?
Any glaring mistakes on my part?
Your code contains a loop and condition:
function updatetimerdisplay() {
$(".auctiondiv .auctiondivleftcontainer .countdown").each(function () {
...
if (newValue <= 1) {
//$(this).parent().fadeOut();
clearInterval(timer);
}
...
}
}
It's very likely that one of these elements have a value which satisfy the condition newValue <= 1. In that case, the timer will stop. Even when you change the contents of an input field, the timer will immediately stop after that run.
If you have to support multiple timers, use a wallet of timers (var timers = {};timers.time1=... or var timers = [];timers[0] = ...). If you have to support only a few timeouts, you can even use variables (var timer1=... ,timer2=..., timer3=...;).
You are trying to bind the change event before the elements exist. Put that code inside the ready event handler:
$(document).ready(function () {
timer = setInterval(updatetimerdisplay, 1000);
$('.countdown').change(function () {
timer = setInterval(updatetimerdisplay, 1000);
});
});
You also might want set the timer variable to an identifiable value when the timer is off (e.g. null), so that you can check for that value before you start a timer to prevent from starting multiple timers.

Categories

Resources