how can I set a setInterval as a global variable? - javascript

I just wanna make a button to start adding some text in my body, and a button to stop adding this text.
I figured out that I can use a setTimeout in a function or a setInterval...but I couldn't Clear both of them because of the local scope...I can't declare both of them as a global scope, I want my button.onclick activate them not by default.
/* global document*/
var myStart = document.querySelector('#start'),
myEnd = document.querySelector('#end'),
myRepeat;
function start() {
"use strict";
document.body.innerHTML += '<br>Welcome StackOverFlowMembers!';
myRepeat = setTimeout(start, 1000)
}
function stop() {
"use strict";
clearTimeout(myRepeat);
}
myStart.onclick = start;
myEnd.onclick = stop;
<body>
<button id="start">Start!</button>
<button id="end">End!</button>
<script src="script.js"></script>
</body>

The problem here is not the scope of the variables. Everything is actually fine with your code on that.
The problem is here:
document.body.innerHTML += '<br>Welcome StackOverFlowMembers!';
If you replace it with:
console.log('Hello!');
Both buttons will work normally. Check this fiddle.
Basically, when you use innerHTML you destroy the event listeners. You can find more on that in this answer

As #anpel's answer explains, your innerHTML call is trashing your event listeners. In the code below, I work around that by putting the onclick attribute directly into the HTML button elements. The boolean doRepeat variable governs whether a subsequent timeout gets initiated.
/* global document*/
var myStart = document.querySelector('#start'),
myEnd = document.querySelector('#end'),
doRepeat;
function start() {
"use strict";
document.body.innerHTML += '<br>Welcome StackOverFlowMembers!';
if (doRepeat) {
setTimeout(start, 1000);
}
}
<body>
<button id="start" onclick="doRepeat=true;start();">Start!</button>
<button id="end" onclick="doRepeat=false;">End!</button>
<script src="script.js"></script>
</body>
Alternatively, you can make a separate <div> into which your function writes its text-- instead how you're doing innerHTML to the entire HTML body, which destroys all child listeners-- and you won't have to worry about your event listeners getting destroyed since those listeners aren't on children of the target div.
Here's a JS Fiddle to demonstrate that: https://jsfiddle.net/j9voxg7s/

Related

How do you integrate an incrementing variable into a paragraph

I am trying to make an idle game rather like candy box. I will have a number at the side of the page which rises by one every second. However, the code shown below does not seem to be working. Could anyone tell me why it is not working; how to fix it and where they got their info from. Thank you in advance.
<script type="text/javascript">
var i = 0;
function increment(){
i++;
document.getElementById('money').innerHTML = i;
}
setInterval(increment(), 1000);
</script>
<h2><u>The best game of the century</u></h2>
<p>you have £<span id="money"></span>.</p>
</body>
</html>
You have two issues:
You are calling your script before the DOM is rendered, so at the point the script runs, there is no element with ID of money.
In your setInterval call, you only need the function name (increment) without the parentheses. Including the parentheses (as increment()) only calls the function at that specific moment, rather than referencing it to be called at each interval. (See the Microsoft page on setInterval for more detailed information.)
See this code:
<h2><u>The best game of the century</u></h2>
<p>you have £<span id="money"></span>.</p>
<script type="text/javascript">
var i = 0;
function increment() {
i++;
document.getElementById('money').innerHTML = i;
}
setInterval(increment, 1000);
</script>
change setInterval(increment(), 1000); to setInterval(increment, 1000);
Its not working because as per document https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval
it takes a function reference to be executed.. but calling the function increment() like this will only execute once and the return of that function will be used which will be null and not intended hope this clears it
var i = 0;
function increment(){
//console.log(i);
i++;
document.getElementById('money').innerHTML = i;
}
setInterval(increment, 1000);
<h2><u>The best game of the century</u></h2>
<p>you have £<span id="money"></span>.</p>

Javascript: Return original innerHTML on mouseout

I am sorry for this very basic question. I am very new to javascript and learning it.
I am stuck with one easy problem-
This is what i am trying to do-
I have a header that has some innertext
<h1 id="bd" onmouseover="fun1()" onmouseout="fun2()"> sample</h1>
I am chaging innerHTML of this header on mouseover like this-
function fun1()
{
document.getElementById("bd").innerHTML="a";
}
well on mouseout i do the same but for getting original innerHTML for this header tag.
function fun2()
{
document.getElementById("bd").innerHTML=document.getElementById("bd").innerHTML;
}
But onmouseout function shows me changed innerHTML, that is a in this case.
How do i get original innerHTML sample again onmouseout?
I want this to be done in javascript.
I tried another way more
function fun1()
{
document.getElementById("bd").innerHTML="a";
}
function fun3()
{
var ds=document.getElementById("bd").innerHTML;
alert(ds);
}
function fun2()
{
document.getElementById("bd").innerHTML=fun3();
}
but it is not working also.
A very generic version would be the following:
First change your markup a bit:
<h1 id="bd" onmouseover="fun1(this)" onmouseout="fun2(this)"> sample</h1>
This way you don't need to look up your element again in your callback function. This works then for more than one element you mouse over. Then you go:
function fun1(elm) {
if (!fun1.cache) fun1.cache = {}; // extend your function with a cache
fun1.cache[elm.id] = elm.innerHTML; // write into cache -> fun1.cache.bd
elm.innerHTML = 'a';
}
function fun2(elm) {
if (fun1.cache && fun1.cache[elm.id]) { // see if cache exists and...
elm.innerHTML = fun1.cache[elm.id]; // read from it
}
}
This way you build a caching system that doesn't need an extra global variable but stays closer to your function.
The next step would be to use only one function and send the new value as a parameter. Create something like a toggle function:
<h1 id="bd" onmouseover="fun(this, 'a')" onmouseout="fun(this)"> sample</h1>
and then your function:
function fun(elm, newValue) {
if (!fun.cache) fun.cache = {};
var value = newValue || fun.cache[elm.id]; // no newValue means recover old value
fun.cache[elm.id] = elm.innerHTML; // alway save the old value
elm.innerHTML = value;
}
If you need more explanations about this and creating Objects just leave a comment to this answer and I'll come back with more details...
Good luck!!
Store the first innerHtml in a global variable ,and use this variable to backup the first innerHtml.
var oldInner = '';
function fun1()
{
oldInner = document.getElementById("bd").innerHTML;
document.getElementById("bd").innerHTML="a";
}
function fun2()
{
document.getElementById("bd").innerHTML=oldInner;
}
You'll need to store the original value somehow:
var originalInnerHTML = "";
function fun1()
{
originalInnerHTML = document.getElementById("bd").innerHTML;
document.getElementById("bd").innerHTML="a";
}
function fun2()
{
document.getElementById("bd").innerHTML=originalInnerHTML
}
Currently you just get the existing innerHTML and set it as the new innerHTML - it's always going to be the same. So this line never changes anything:
document.getElementById("bd").innerHTML=document.getElementById("bd").innerHTML;
once you have changed the innerhtml of an element, old data is gone
In order to get the old data you first need to have it store in some other place.
For ex : on mouseover you can first copy the original html to a hidden div, and upon mouseout you can again copy from the hidden div to the main div.
hope this helps.
A very easy implementation will be to have the two text you want to display in the div. so you have:
<h1 id="bd">
<span id='sample1'>sample1</span>
<span id='sample2' style='display: none'>a</span>
</h1>
var el = document.getElementById("bd");
el.onmouseover = function() {
document.getElementById("sample1").setAttribute("style", "display: none");
document.getElementById("sample2").setAttribute("style", "");
}
el.onmouseout = function() {
document.getElementById("sample2").setAttribute("style", "display: none");
document.getElementById("sample1").setAttribute("style", "");
}

Passing a variable into a DOM function in Javascript

I took this example from w3schools and modify it to this. The code below is not working.
What I intend to do is hide the div with id "demo1". It is not working. What is the problem?
<!DOCTYPE html>
<html>
<head>
<script>
function myFunction(div_id)
{
//here the div_id variable seems to unparsable by the DOM event
document.getElementById(div_id).innerHTML = hello;
}
</script>
</head>
<body>
<p>Click the button to trigger a function.</p>
<button onclick="myFunction('demo1')">Click me</button>
<div id="demo1"></div>
<div id="demo2"></div>
</body>
</html>
The variable hello is not defined. You were probably looking to set the innerHTML as a String:
function myFunction(div_id) {
document.getElementById(div_id).innerHTML = "hello";
// -----------------------------------------^-----^
}
DEMO: http://jsfiddle.net/uzuKp/1/
Even though you took an example from W3Schools and modified it, I'd suggest binding events separate from the HTML and storing associated data in data-* attributes. In your example, it can be as something like this:
<p>Click the button to trigger a function.</p>
<button data-div-id="demo1">Click me</button>
<button data-div-id="demo2">Click me</button>
<button data-div-id="demo1">Click me</button>
<div id="demo1">demo1</div>
<div id="demo2">demo2</div>
And the JS:
function clickHandler() {
var targetDivId, targetDiv;
targetDivId = this.getAttribute("data-div-id");
targetDiv = document.getElementById(targetDivId);
targetDiv.innerHTML = "Hello" + new Date().getTime();
}
function loadHandler() {
var buttons, i, j, cur;
buttons = document.getElementsByTagName("button");
for (i = 0, j = buttons.length; i < j; i++) {
cur = buttons[i];
cur.onclick = clickHandler;
}
}
window.onload = loadHandler;
DEMO: http://jsfiddle.net/3K4RD/
Although I would also suggest looking at the following article to see different ways to bind events: addEventListener vs onclick
One final suggestion I have is to not set the innerHTML property. You may have a simple example here, but it's usually a better idea to use DOM methods like appendChild (to add a node) and document.createTextNode (to create text that can be appended). Of course, that would require the contents to be cleared out first, something like:
while (targetDiv.firstChild) {
targetDiv.removeChild(targetDiv.firstChild);
}
targetDiv.appendChild(document.createTextNode("Hello"));
DEMO: http://jsfiddle.net/52Kwe/
You could also store the specific string that needs to be set as the innerHTML as a data-* attribute (especially if it differs between buttons).
UPDATE:
Per your recent edit, the style property is a special property, which is actually a special object with style properties that you need to set. So for your example, you have to set the .style.display value, like:
document.getElementById(div_id).style.display = "none";
document.getElementById(div_id).style.display = 'none';
document.getElementById(div_id).style.visibility= 'hidden';

$("#id").click(function(){calculation()});

What is wrong with the line in the header?
The below example is supposed to make a button which will increment a counter each time it is clicked. However, I enforce a delay of 2000 ms between button clicks. The version below works, however, if I use the commented out line instead of
document.getElementById("rollButton").onclick=function(){calculation()};
(both in function afterWaiting())
I get various odd results, for instance that the counter starts incrementing by a lot more than 1, and the waiting time disappears?
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
function afterWaiting()
{
$("#rollButton").css("color","black");
//$("#rollButton").click(function(){calculation()});
document.getElementById("rollButton").onclick=function(){calculation()};
}
var counter=0;
function calculation()
{
////Enforcing wait:
document.getElementById("rollButton").style.color="red";
document.getElementById("rollButton").onclick="";
window.setTimeout("afterWaiting()",2000);
counter=counter+1;
document.getElementById("test").innerHTML=counter;
}
</script>
</head>
<body>
<button type="button" onclick="calculation()" id="rollButton"> Roll! </button>
<p id="test"> </p>
</body>
</html>
What have I misunderstood?
thanks in advance :)
JSFiddle:
http://jsfiddle.net/Bwxb9/
The difference is that when you apply event handlers through onclick as you do in your original version, you can only bind one handler to the element. And using onclick="" kind of clears it.
When using jQuery .click(handler) you bind a new handler each time you call it (and you can unbind it with unbind('click') (and not with onclick=""). So after a couple of calls to afterWaiting you have applied mulitple click handlers on your element, and on each click the calculation function runs multiple times..
So, one way to correct it is to replace
document.getElementById("rollButton").onclick="";
with
$('#rollButton').unbind('click');
The only code required is
<button type="button" id="rollButton"> Roll! </button>
<p id="test"> </p>
var counter = 0;
var $test = $('#test');
var $rollButton = $('#rollButton');
function increment(){
$test.html(counter++);
$rollButton.off('click', increment);
setTimeout(function(){
$rollButton.on('click', increment);
}, 2000);
}
$rollButton.on('click', increment);
Demo: Fiddle
Updated: as suggested by Andy, but I would recommend Andy's answer as it involves no additional event manipulation
var counter = 0;
var $test = $('#test');
var $rollButton = $('#rollButton');
function increment(){
$test.html(counter++);
setTimeout(function(){
$rollButton.one('click', increment);
}, 2000);
}
$rollButton.one('click', increment);
Demo: Fiddle
That's generally a bit of an odd and confusing approach.
Here's how i'd do it, without mixing jquery and pure js (onclick) too much:
http://jsfiddle.net/LGvKS/
var wait = false;
counter = 0;
$('button').click(function(){
if(!wait){
$('span').text(++counter);
wait=true;
setTimeout(function(){
wait=false;
},2000);
}
});

Why Doesn't This Javascript Work? Style Change

I cant for the life of me figure out why this doesn't work:
javascript:
//==================================//
// resize_middle //
//----------------------------------//
// Resizes the content and left //
// navigation div to make up the //
// remains of available space. //
//==================================//
function resize_middle()
{
min_height = (window.innerHeight - 276) + "px";
middle_left = document.getElementById("middle_left");
middle_right = document.getElementById("middle_right");
alert("its not going to work!");
alert("here goes...");
alert(min_height);
middle_left.style.minHeight = min_height;
alert("it works!");
middle_right.style.minHeight = min_height;
}
//==================================//
// event handlers //
//==================================//
window.onload = resize_middle();
window.onresize = resize_middle();
html(body & javascript bit in head shown only):
<script src="javascript.js" type="text/javascript" charset="utf-8"></script>
<body>
<div id="container">
<div id="central_column">
<div id="top_left">
<img src="./images/icon.png" alt="icon" style="width:100%;height:auto;" />
</div>
<div id="top_right">
top right
</div>
<div id="middle_left">
middle left
</div>
<div id="middle_right">
middle right
</div>
<div id="bottom">
bottom
</div>
</div>
</div>
</body>
I'v used this before and have a working copy of some only slightly different code, but it works perfectly. I get the debugging alerts up until "it works!", which I don't get. Thanks in advance, ell.
You need this instead:
window.onload = resize_middle;
window.onresize = resize_middle;
Because as having it resize_middle() the function is processed immediately and the result is added to the event. You want the function itself to be added to the event so you leave off the () unless your function returns a function for the event to use.
You need this instead:
window.onload = resize_middle;
window.onresize = resize_middle;
Currently you're calling those functions and assigning their return value to the event. What you want is to assign the functions themselves to the events, and let the events call them.
Side note:
Unless the variables in resize_middle are defined elsewhere and you intend for them to be accessible to the outer scope, it is good practice to use the var keyword when defining new variables.
function resize_middle()
{
// Changed to use "var"
var min_height = (window.innerHeight - 276) + "px";
var middle_left = document.getElementById("middle_left");
var middle_right = document.getElementById("middle_right");
alert("its not going to work!");
alert("here goes...");
alert(min_height);
middle_left.style.minHeight = min_height;
alert("it works!");
middle_right.style.minHeight = min_height;
}
window.onload = resize_middle;
Read this resource on assigning event handlers.

Categories

Resources