setInterval function cannot use outer variable - javascript

Why is prevColor always undefined?
This code should log a new color (actualColor) and the previous color (prevColor). However I cannot save into prevColor from inside the setInterval function. Where is the bug? I don´t think it is a context problem. But I´m not sure. There´s no this inside...
Can you tell me how I can save the value of actualColor in prevColor from inside the setInterval function?
var actualColor;
var prevColor;
// do some user action and change actualColor
setInterval(function () {
// only log the color if it differs from prevColor
if (actualColor != prevColor) {
console.log("actualColor: " + actualColor + ", prevColor: " + prevColor);
}
prevColor = actualColor;
}, 100);
Console:
actualColor: acff06, prevColor: undefined

I think it must be your context -I made a simple webpage with all the bits you have above, and it works fine -even without setting initial values on the variables:
I placed your code in a script tag in the HEAD, and added
<input
type="text"
id="actualColor"
/>
<input
type="button"
onclick = "actualColor = document.getElementById('actualColor').value;"
value = "change colour" />
to provide a way of changing actualColor in the webpage (rather than using the console)

You need to initialize the value of the prevColor, otherwise first time it will be undefined
Just do
var actualColor;
var prevColor = "";
// do some user action and change actualColor
setInterval(function () {
// only log the color if it differs from prevColor
if (actualColor != prevColor) {
console.log("actualColor: " + actualColor + ", prevColor: " + prevColor);
}
prevColor = actualColor;
}, 100);

Related

Button to change text back and forth JS

I am trying to make a button that changes temperature to Fahrenheit on click, but if it is clicked again, changes the temperature back to Celsius. If you click the temperature symbol(i.e Celsius) it should change the temperature to Fahrenheit. If the Fahrenheit symbol, it should show the temperature in Celsius again.
The problem is that my current button changed the temperature to Fahrenheit and immediately changes it back to Celsius.
In my research, I found the toggle() jquery function, but it seems that it is now deprecated and, to be honest, I don't really understand how to use it.n
I also found this stackoverflow qustion, but do not know how to apply the answer to my situation: Switch button text back and forth with Bootstrap and jquery
Thanks!
var currentTemp= "cel";
$("#tempUnit").click(function(){
alert("Temperature Changed to Fahrenheit.");
// var currentTemp= cel;
if (currentTemp=== "cel") {
currentTemp = "faren";
var farCalc= (data.main.temp * 1.8) + 32;
$('#temp').html("Temperature:" + Math.round(farCalc) +"");
$('#tempUnit').html("&#8457");
}
if (currentTemp=== "faren"){
alert("Temperature Changed to Celsius");
$('#temp').html("Temperature:" + data.main.temp +"");
$('#tempUnit').html("&#8451");
}
See full code here: https://codepen.io/mso122591/pen/XZZWPR
The problem is that my current button changed the temperature to Fahrenheit and immediately changes it back to Celsius.
This happens because you are setting currentTemp = "faren" in the condition if (currentTemp=== "cel") which return to true for the first time and then you are again using if condition instead you should use else block like this
var currentTemp = "cel";
$("#tempUnit").click(function() {
alert("Temperature Changed to Fahrenheit.");
// var currentTemp= cel;
if (currentTemp === "cel") {
currentTemp = "faren";
var farCalc = (data.main.temp * 1.8) + 32;
$('#temp').html("Temperature:" + Math.round(farCalc) + "");
$('#tempUnit').html("&#8457");
}
else {
currentTemp = "cel";
alert("Temperature Changed to Celsius");
$('#temp').html("Temperature:" + data.main.temp + "");
$('#tempUnit').html("&#8451");
}
P.S Again set currentTemp = "cel"; in else block
First off the codepen looks broken and honestly extremely difficult to read. I gave up before trying. So I'm will answer in a way that describes how I would approach the problem.
First break up your responsibilities into different functions. Then connect those functions together. You will be managing state (in this case which degree your currently on. And finally attaching to the DOM with the results and the event handler for when the user clicks. Each is it's own self contained function.
$(function() {
var TEMP_SYMBOLS = {
celceus: '&#8451',
fahrenheit: '&#8457'
};
var TEMP_CONVERTERS = {
celceus: function(temp) { return temp; },
fahrenheit: function(temp) { return (temp * 1.8) + 32; }
};
var currentTemp = 0;
var currentTempMode = 'celceus';
function fetchTemp() {
// Here is where you fetch the temp from AJAX.
// For demonstration purposes we will simply hard code a value.
currentTemp = 32.4;
}
function renderTemp() {
var symbol = TEMP_SYMBOLS[currentTempMode];
var converter = TEMP_CONVERTERS[currentTempMode];
var value = converter(currentTemp);
$('#temp-output').html('Temperature: ' + value + ' ' + symbol);
}
fetchTemp();
renderTemp();
$('#temp-output').on('click', function() {
currentTempMode = currentTempMode === 'celceus' ? 'fahrenheit' : 'celceus';
renderTemp();
});
});
#temp-output {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="temp-output">Loading…</span>

Appending HTML element with Javascript, element isn't seen?

I'm still new to javascript, so expect to see a lot of bad programming practices...
function addButton(name, state) {
numButtons += 1;
if (name === '') {
var i = 0;
while(buttonStates.hasOwnProperty("button" + i) === true) i++;
name = "button" + i;
}
document.getElementById("buttonTable").innerHTML +=
"<tr>\
<td>\
<p id=\"buttonName" + numButtons + "\" class=\"buttonName\">" + name + "</p>\
</td><td>\
<button id=\"" + name + "\" onclick=\"setButton(this, 'toggle')\" class=\"button\">OFF</button>\
</td>\
</tr>";
//===IMPORTANT AREA 1==========================================================//
//setTimeout(function(){setButton(document.getElementById(name), state)}, 0); //works
setButton(document.getElementById(name), state); //doesn't work, no change
//=============================================================================//
}
function setButton(elem, state) {
//this is where the button's colors are set to light colors
//notice how I am using elem.style.backgroundColor
if(String(state) === 'toggle') state = !buttonStates[elem.id];
if (String(state) === 'true') {
buttonStates[elem.id] = true;
elem.style.backgroundColor = "rgb(112,192,112)";
elem.textContent = 'ON';
} else {
buttonStates[elem.id] = false;
elem.style.backgroundColor = "rgb(255,128,128)";
elem.textContent = 'OFF';
}
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
document.getElementById("demo").innerHTML = this.responseText;
//===IMPORTANT AREA 2==========================================================//
if (buttonStates[elem.id] === true) {
elem.style.backgroundColor = 'green'; //doesn't work, no change. Notice how I am, again, using elem.style.backgroundColor
//document.getElementById(elem.id).style.backgroundColor = 'green'; //works
} else {
elem.style.backgroundColor = 'red';
}
//=============================================================================//
} else {
document.getElementById("demo").innerHTML = "readyState: " + this.readyState + "<br>status: " + this.status;
}
};
xhttp.open("POST", "/buttonToggle", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send(elem.id + "=" + buttonStates[elem.id]);
}
So basically I add an HTML button in addButton(), which calls setButton() and passes the new element to that function. In setButton(), it first sets the element's color to light-green or light-red, indicating that the server has not yet updated the state of the button. This part works fine, the button's color DOES change. HOWEVER, in the callback function for the server request, the button refuses to change color in the exact same method as used before. I have found ways around it (either by using setTimeout() with a time of 0ms, or by using the passed element to again get the element from document), but I want to know WHY this happens. It seems to only be a problem in the callback function. I have verified that the callback function does execute and that the code for changing the color is executed.
As a side note, if I have multiple buttons, the last button works fine and all those before it do not work, their color is still light-green or light-red.
Notice buttons 0-5 are all a light color, yet button6 is solid, WHY?!?
Different elements on the HTML doc should have different unique ids. Your all buttons have the same id so only last one is working.
Make your ids unique by using 'button' + i like you are using name.
Okay, I don´t get the current error and why your code doesn´t work. Maybe we need to see the whole code?
But you are definitely searching for the keyword "event loop".
When your code works when you are running it in a timeout, your code needs to be running async.
See more details here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
If you post the whole code, I will take a look again, but maybe this already helps.
And if this are your first steps in programming, you are doing it great!

Updating Momentjs FromNow() in a div element

I'm having this div element that shows the time past since it got created. However it doesn't get updated and always remains on few seconds ago. It looks like this
var newMsg= "<div id="chat-time">'+ moment().fromNow()+'</div>";
$("#chat-list").html( newMsg);
How can I update this text. I know I can do it with sentInterval but I can't figure out how to do it properly.It just prints out seconds! I'm using this for a chatroom. So each message will have a timestamp in the formatof momentjs.fromNow().
Does setting timer for all these message create a problem? I'd appreciate a hint.
EDIT:I'm using this code as mentioned in below but it's not showing anything:
<div id="chat-time"></div>
var messageTimeStamp = new Date();
setInterval(function(){
var time = moment(messageTimeStamp).fromNow();
$("#chat-time").html(time);
}, 1000);
To make this work you need the element in the dom and the setInterval running without being included in any string concatenation
HTML
<div id="chat-time"></div>
JS
var $chatTime = $('#chat-time').text(moment().fromNow());
setInterval(function(){
var time = moment().fromNow();
$chatTime.txt( time );
}, 1000);
UPDATE 2
Given that you're using socket.io, you'd do something like this (demo: http://plnkr.co/edit/QuaMV6x1vNB0kYPaU6i1?p=preview):
// The messages the user can currently see.
var messages = [];
// You have something like this in your code, presumably.
socket.on('new message', function(data) {
addChatMessage(data);
});
function addChatMessage(data) {
// First add the message to the dome, with a unique id for the timestamp text.
var messageElementId = 'chat-message-' + data.messageId;
$("#chat-list").prepend($("<div>" + data.message + "<i> (sent: <span id='" + messageElementId + "'>just now</span>)</i></div>"));
// When you no longer display that message in the DOM it from clear this array. I'd render the DOM based on this list if I were you.
messages.push({
messageElementId: messageElementId,
timestamp: data.timestamp
});
}
// By updating all the messages at once you don't have memory leaks.
setInterval(function() {
messages.forEach(function(message) {
var time = moment(message.timestamp).fromNow();
$("#" + message.messageElementId).text(time);
});
}, 1000);
UPDATE 1
Given this is your code:
var newMsg= "<div id="chat-time">'+ moment().fromNow()+'</div>";
$("#chat-list").html(newMsg);
You would do this, instead:
var messageTimeStamp = new Date(); // You need to grab this from somewhere.
setInterval(function(){
var time = moment(messageTimeStamp).fromNow();
$("#chat-list").html(time);
}, 1000);
You need to use moment(TIMESTAMP_OF_MESSAGE) not moment() and do something like this:
$(function(){
$("body").append($('<div id="chat-time"></div>'));
var messageTimeStamp = new Date();
var i = 0;
setInterval(function(){
var time = moment(messageTimeStamp).fromNow();
$("#chat-time").html('moment().from(messageTimeStamp): ' + time + '; setInterval calls made ' + i++);
}, 1000);
});
Here's a demo.
http://plnkr.co/edit/QuaMV6x1vNB0kYPaU6i1?p=preview
I dont see any problem using setInterval (). AngularJS wrapper setInterval on $interval service module . Check out these urls: interval Angular and Wrapper SetInterval

AJAX returns previous value in array

I'm making a website to host artwork. The idea is that when the page loads I have JavaScript run a php file that makes a query to the server to get the names and IDs of the image files (artwork.jpg) and display them as thumbnails on a page.
When you scroll over the thumbnail, the artwork is displayed larger on a different part of the screen and the description, specs, etc for the piece of art fades in. My issue is that when I make this second AJAX call it appends the value of the previously moused over image to the screen and does nothing until you've moused over at least two images.
Here's my code for the first ajax call that appends thumbnails to the page and creates a form with the value of the thumnbnail's id:
function getArtDescriptions()
{
$.post('../../path/to/script/get_art.php', function(json)
{
if (json.art.length > 0)
{
$.each(json.art,function()
{
var info =
'<div class = "thumbnail_box">'
+ '<img src = "images/thumbnails/'
+ this['img']
+ '"id = "'
+ this['ID']
+ '"> '
+ '<form id = "art_descriptions'
+ this['ID']
+ '" '
+ 'name = "art_descriptions'
+ this['ID']
+ '">'
+ '<input type = "hidden" id = "descriptions" name = "descriptions" value = "'
+ this['ID']
+ '"></form>'
+ '</div>';
});
}
}, 'json');
}
And this is the code I'm using to make the second AJAX call that is giving me a problem:
setTimeout(function get_id()
{
var tooltipTimeout;
$(".thumbnail_box img").on("mouseenter", function()
{
tooltipTimeout = setTimeout(details(this.id),0);
console.log(this.id);
});
$(".thumbnail_box img").on("mouseleave", function()
{
hideTooltip();
});
function hideTooltip()
{
clearTimeout(tooltipTimeout);
$(".description").fadeOut().remove();
}
}, 800);
//GRAB DESCRIPTIONS FROM DATABASE AND
function details(art)
{
var formname = "#art_descriptions"+art;
var filename = '../../file/path/to/script/get_descriptions.php';
//console.log($(formname).serialize());
$(".thumbnail_box img").on("mouseenter", function()
{
$.post(filename, $(formname).serialize(), function(json)
{
if (json.descriptions.length > 0)
{
//MAKE SURE TO EMPTY OUT DIV CLASSES FOR EACH TAB
$(".description").empty();
$.each(json.descriptions,function()
{
console.log("art method"+this['ID']);
$(".description").append(this['description']+this['ID']).hide().fadeIn("fast");
});
}
}, 'json');
});
};
When I console.log(this['ID']) in the get_id() method the correct value is displayed in the console, but when I console.log("art method"+this['ID'] in the details method I get a value equal to the previously scrolled over thumbnail's ID. I'd really appreciate any insight on this issue.
Is it something to do with the use of setTimeout()? My code would not run without specifying a timeout for the method. For example if I load the page and then scroll over images with ID's 14 and then 13, my console will display:
14
13
art method 14
The issue is that you are appending more of the same events. After the first mouseenter event occurs the details function is called, which then appends another mouseenter event. So subsequent calls will be doing the same thing. You can see an example of this here: http://jsfiddle.net/6qre72fk/.
var counter = 0;
$('#container1').on('mouseenter', function(){
$('#container2').text('First mouseenter');
appendingAnotherMouseEnter();
});
function appendingAnotherMouseEnter(){
$('#container1').on('mouseenter', function(){
$('#container2').text(counter);
counter++;
});
}
You can see how the counter is incremented several times due to all appended the mouseenter events.

jQuery - removing an alert stop code from working

The code below works, but there is an issue with it.
That issue is that unless the alert(this.href); - (about line 11) is in the code the following function does not work.
//There are pages which make up 2 chapters in this content
//We shall attempt to grab all the links from these pages
var c;
var chapters = new Array();
chapters[0] = "original/html/0/ID0EFJAE.html";
//Loop through each page of links
$.each(chapters, function(key, value) {
$("#theContent").append("<div class='chapterindex" + key + "'>working</div>");
$(".chapterindex" + key).load(value + " .content");
alert(this.href);
$(".chapterindex" + key + " div.link a").each(function(intIndex) {
alert(".chapterindex" + key);
});
});
If I take the first alert out of line 11 then the last alert doesn't fire. What am I doing wrong?
The delay that the alert is causing is allowing the data in your load call to load. I suspect that when you remove the alert the data does not load in time.
Try using a callback with your load call, something like (code not tested) -
$(".chapterindex" + key).load(value + " .content",function () {
$(".chapterindex" + key + " div.link a").each(function(intIndex) {
alert(".chapterindex" + key);
});
});
The first alert is probably giving the load function enough time to finish so when you take it out the load function is not done when its trying to fire your second alert.

Categories

Resources