Only starting a function on button click?(javascript) - javascript

How would I make a start button for a function? I have 9 different functions for different animations on my page. I need to figure out how execute the animations only when a button is clicked(a start button) The reason why I want to do this is because I'm making a simple game, but I'd like the end user to be able to interact with the elements of the game before they start(I already have this done with jQuery, but at the moment I can only move the elements while the game is running which isn't what I want to do.) A quick example of the animate function is
function animate0(pos) {
pos %= urls.length;
var animation0 = document.getElementById('animation0');
var counter = document.getElementById('counter');
animation0.src = urls[pos];
if (pos == 1) {
animation0.onclick = function() {
counter.innerHTML = parseInt(counter.innerHTML) + 1;
}
}
else {
animation0.onclick = function() {
//do nothing
}
}
setTimeout(function() {
animate0(++pos);
}, (Math.random()*500) + 1000);
}
Then to execute the animation I use this
window.onload = function() { //Frames go below, seperated by commas format= , "URL");
urls = new Array("http://i51.tinypic.com/sxheeo.gif", "http://i56.tinypic.com/2i3tyw.gif");
animate0(0);
To display the animation on the page,
<img id='animation0' src ='http://i51.tinypic.com/sxheeo.gif'/>
Thanks!

document.getElementById('start').onclick = function(){
animate0(0);
}
This is assuming you have an element with id='start'
Here is a fiddle: http://jsfiddle.net/maniator/TQqJ8/13/

I think I may be misunderstanding your question, but if for instance you had this button (or indeed just about any other element):
<input type="button" id="theButton" value="Click Me">
Then you can hook up a handler for its click event in any of several ways.
The simplest, but least powerful, is DOM0-style:
document.getElementById("theButton").onclick = function() {
animate0(0);
return false;
};
The problem with DOM0 handlers is that there can only be one handler/event type on each element.
DOM2-style, which on standards-based browsers looks like this:
document.getElementById("theButton").addEventListener('click', function(event) {
event.preventDefault();
animate0(0);
}, false);
...and on older IE looks like this:
document.getElementById("theButton").attachEvent('onclick', function() {
animate0(0);
return false;
});
Note the differences, the event name is "click" in the DOM2 standard, "onclick" for Microsoft, and the event object is passed in the first argument in the DOM2 standard, but it's a global variable (I didn't use) on Microsoft's older browsers. (Not dissing Microsoft, in the IE5 - IE6 timeframe, they did a lot to innovate web browsers, including ajax, innerHTML, and in fact multiple handlers per event per element via attachEvent, which predates the DOM2 standard. It's just after IE6 that they let the ball drop for...well...more than a decade.)
In all of those cases, I've called animate0 directly, since it does its first loop and then schedules the next bit itself. You could, of course, use setTimeout to schedule even the first bit to be asynchronous (probably just use a delay of 0, which will be 5-10 ms on most browsers). It depends on what you want to do.

Related

HTML script constantly running?

Is there a way to have javascript coffee always be checking the document? What I mean by this is if I have "if" statements, it will always check the document to see if it needs to run the script...
Also, it needs to not be a function that needs to be called. Here is some simple example of what I'm asking.
<button id="btn1">random button</button>
<button id="btn2" onclick="hide()">hide</button>
<script>
function hide() {
document.getElementById("btn1").setAttribute("hidden", "true")
}
</script>
<script>
if (document.getElementById("btn1").hidden === true) {
document.write("random button is hidden")
}
</script>
How would I do something like this? I don't want this specifically, this is just an example.
In the code,a button hides another one, and there is script always checking if it is hidden. Please let me know.
edit
I see other people's answers, but it's not what I need. Is it possible to have functions constantly run without being called?
JavaScript features what's called events. Any DOM element may emit events in response to user interaction. For example:
var button2 = document.querySelector('#btn2');
// what event function to be called
// vvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
button2.addEventListener('click', function runThisOnButtonClick(event) {
// Code in here will only run when the button is clicked.
hideInSomeFashion(button1);
});
If you want a more general view of the DOM, recent browsers have what's called a MutationObserver.
In your case, something like this [example]:
// Older versions of Webkit have this under a prefix
var MutationObserver = MutationObserver || WebkitMutationObserver;
var myMo = new MutationObserver(function(mutations) {
// handleMutation in this case is a function that takes
// a mutation and performs an action. Note that in this
// case, return values are ignored.
mutations.forEach(handleMutation);
});
myMo.observe(button1, {
attributes: true // Observe changes in attributes
});
function handleMutation(mutation) {
console.log("Hidden?", mutation.target.hidden);
}
MutationObserver can observe a common ancestor and all of its descendants for changes as well.
Although I strongly recommend finding another way to do what you want, if possible, I think this is what you're asking for.
setInterval(function() {
if(document.getElementById("btn1").hidden === true) {
// Button is hidden
}
}, 1000); // Wait 1000ms before running again

Create DOM element from Object in javascript

Hi I'm trying to understand how to make Dom elemnt, let's say "div" form my data Object.
I've made an object like this:
var div = {
style : {
width: Math.floor(Math.random() * 100),
height: Math.floor(Math.random() * 100),
position: "relative",
top:Math.floor(Math.random()*500)
},
sayHi: function () {
alert("Hello");
},
}
What I need to do now, is to make it live element in DOM with these css params?
Thank you
To create a DOM element, you use document.createElement, like this:
var elm = document.createElement('div');
That elm will already have a property called style; you can then assign to its members, e.g.:
elm.style.width = div.style.width + "px"; // Remember this is CSS, you need units
A for..in loop on your div.style might be useful there, but do be sure to handle the units thing.
To attach event handlers to it, you can do the old DOM0 thing of assigning to onXyz properties, like this:
elm.onclick = div.sayHi;
...which will make it run the sayHi function on click, but a more modern way is via addEventListener:
elm.addEventListener('click', div.sayHi, false);
Older versions of IE don't have addEventListener, but they do have its MS-only predecessor, attachEvent:
elm.attachEvent('onclick', div.sayHi);
Note the difference in the event name, and it doesn't have the third argument.
All of this is academic unless you add elm to the page somewhere. :-) You can do that by getting a reference to another element on the page, and then calling appendChild:
someOtherElement.appendChild(elm);
More to explore:
DOM2 Core
DOM2 HTML
DOM3 Core
HTML5 Web Application APIs
Because of things like the addEventListener / attachEvent browser incompatibility and various other small things, and because they offer a lot of pre-packaged utility functionality, a lot of people (including me) use a JavaScript library like jQuery, YUI, Closure, or any of several others to help with this stuff.
Try this
var DOMdiv = document.createElement("div");
for(var key in div) {
if(key === "style") {
for(var cssstyle in div[key]) {
DOMdiv.style[cssstyle] = div[key][cssstyle];
}
} else {
DOMdiv[key] = div[key];
}
}
document.body.appendChild(DOMdiv);
But keep in mind that this Div has now a function called sayHi() attached to it. There is no eventhandler initiated or whatsoever. If you like to have some eventhandlers, change your object like that:
var div = {
[...]
onclick: function() {
alert("Hi");
}
};

Javascript timeout when no actions from user for specified time

I want to call a js function when there is no activity from user on the web page for specified amount of time. If there is activity from user then reset timeout. I tried to search but couldn't find anything in particular. I am familiar with setTimeout() and clearTimeout() and how they work. What I am looking for is where/how to monitor for user activity. Is there any event in which I can set and clear timer?
Thank you.
Edit #1:
This webpage has one input text box & one button. It's kind of regular chat page. When I say no user activity, I mean that the user has not typed anything in text box or has not pressed any button for specified amount of time. And one more thing that it is targeted for touch based smartphone devices.
Edit #2:
Thank you everyone for suggestions. I've implemented solution based on more than one answers provided. So I will give upvote to all answers that I've found helpful instead of accepting one as answer.
// Using jQuery (but could use pure JS with cross-browser event handlers):
var idleSeconds = 30;
$(function(){
var idleTimer;
function resetTimer(){
clearTimeout(idleTimer);
idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
}
$(document.body).bind('mousemove keydown click',resetTimer); //space separated events list that we want to monitor
resetTimer(); // Start the timer when the page loads
});
function whenUserIdle(){
//...
}
Edit: Not using jQuery for whatever reason? Here's some (untested) code that should be cross-browser clean (to a point; doesn't work on IE5 Mac, for example ;):
attachEvent(window,'load',function(){
var idleSeconds = 30;
var idleTimer;
function resetTimer(){
clearTimeout(idleTimer);
idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
}
attachEvent(document.body,'mousemove',resetTimer);
attachEvent(document.body,'keydown',resetTimer);
attachEvent(document.body,'click',resetTimer);
resetTimer(); // Start the timer when the page loads
});
function whenUserIdle(){
//...
}
function attachEvent(obj,evt,fnc,useCapture){
if (obj.addEventListener){
obj.addEventListener(evt,fnc,!!useCapture);
return true;
} else if (obj.attachEvent){
return obj.attachEvent("on"+evt,fnc);
}
}
This calls for a debouncer:
function debounce(callback, timeout, _this) {
var timer;
return function(e) {
var _that = this;
if (timer)
clearTimeout(timer);
timer = setTimeout(function() {
callback.call(_this || _that, e);
}, timeout);
}
}
Used like this:
// we'll attach the function created by "debounce" to each of the target
// user input events; this function only fires once 2 seconds have passed
// with no additional input; it can be attached to any number of desired
// events
var userAction = debounce(function(e) {
console.log("silence");
}, 2000);
document.addEventListener("mousemove", userAction, false);
document.addEventListener("click", userAction, false);
document.addEventListener("scroll", userAction, false);
The first user action (mousemove, click, or scroll) kicks off a function (attached to a timer) that resets each time another user action occurs. The primary callback does not fire until the specified amount of time has passed with no actions.
Note that no global flags or timeout variables are needed. The global scope receives only your debounced callback. Beware of solutions that require maintenance of global state; they're going to be difficult to reason about in the context of a larger application.
Note also that this solution is entirely general. Beware of solutions that apply only to your extremely narrow use case.
Most JavaScript events bubble, so you could do something like the following:
Come up with a list of all the events you'd consider to be "activity from the user" (e.g., click, mousemove, keydown, etc.)
Attach one function as an event listener for all of those events to document (or maybe document.body for some of them; I can't remember if that's an issue or not).
When the listener is triggered, have it reset the timer with clearTimeout/setTimeout
So you'd end up with something like this:
var events = ['click', 'mousemove', 'keydown'],
i = events.length,
timer,
delay = 10000,
logout = function () {
// do whatever it is you want to do
// after a period of inactivity
},
reset = function () {
clearTimeout(timer);
timer = setTimeout(logout, 10000);
};
while (i) {
i -= 1;
document.addEventListener(events[i], reset, false);
}
reset();
Note that there are some issues you'd have to work out with the above code:
It's not cross-browser compatible. It only uses addEventListener, so it won't work in IE6-8
It pollutes the global namespace. It creates a lot of excess variables that might conflict with other scripts.
It's more to give you an idea of what you could do.
And now there are four other answers, but I've already typed it all up, so there :P
You want to monitor events like mousemove, keypress, keydown, and/or click at the document level.
Edit: This being a smartphone app changes what events you want to listen for. Given your textbox and button requirements, I'd listen to oninput and then add the resetTimeout() call to the click handler for your button.
var inactivityTimeout = 0;
function resetTimeout() {
clearTimeout(inactivityTimeout);
inactivityTimeout = setTimeout(inactive, 300000);
}
function inactive() {
...
}
document.getElementById("chatInput").oninput = resetTimeout;
Something like this:
function onInactive(ms, cb){
var wait = setTimeout(cb, ms);
// Bind all events you consider as activity
// Note that binding this way overrides any previous events bound the same wa
// So if you already have events bound to document, use AddEventListener and AttachEvent instead
document.onmousemove = document.mousedown = document.mouseup = document.onkeydown = document.onkeyup = document.focus = function(){
clearTimeout(wait);
wait = setTimeout(cb, ms);
};
}
IE: http://jsfiddle.net/acNfy/
Activity in the bottom right frame will delay the callback.
I'm using a nifty little 'delay' method for this that I found in this thread
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
use like
delay(function(){ doSomethingWhenNoInputFor400ms(); },400);
Also, take a look at jQuery idleTimer plugin from Paul Irish (jquery.idle-timer.js). It was based on Nicholas C. Zakas' Detecting if the user is idle with JavaScript and YUI 3 article (idle-timer.js).
It looks at similar events to the other answers, plus a few more.
events = 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove';
// activity is one of these events

setting events programmatically

I am setting the className of a table row in my code, is it possible to do something similiar to set an event on a row? This is along the lines of what I would like to do :
for (var i = 1; i < numRows; i++) {
var ID = table.rows[i].id;
if (i % 2 == 0) {
table.rows[i].className = "GridRow";
table.rows[i].onmouseout = "GridRow";
}
else {
table.rows[i].className = "GridRowAlt";
table.rows[i].onmouseout = "GridRowAlt";
}
}
Yes, you can assign a function instance to the event handler that way:
table.rows[i].onmouseout = function() { ... };
Be careful doing that in loops, because you're creating a new function on every loop and the function closes over the data in scope (and so has an enduring reference to it, not a copy of it as of when the function was created; see this other recent question for more). But don't worry, closures are not complicated once you understand how they work.
In general, this is called "DOM0" event handling because it involves a method of attaching event handlers that was created prior to the first DOM specification. As of DOM2, there's a better way addEventListener:
table.rows[i].addEventListener('mouseout',function() { ... }, false);
It's "better" because you can have more than one event handler on the same event of the same element, whereas with the DOM0 mechanism, assigning a new event handler disconnects the previous one (if any).
On IE prior to IE9, sadly, addEventListener wasn't supported but it did have the very similar attachEvent:
table.rows[i].attachEvent('onmouseout',function() { ... });
Note the differences:
addEventListener's event names don't have the "on" prefix
addEventListener has one more param than attachEvent, which you almost always want to set false
Update:
All of the examples above are for inline anonymous functions, which is a bit unlike me, because I don't like anonymous functions. So just for clarity, from an events perspective, a function is a function. It can be a named function you declare elsewhere, or an inline anonymous function, whatever:
// Hook up...
table.rows[i].addEventListener('mouseout', handleRowMouseOut, false);
// Nice, reusable function defined elsewhere
function handleRowMouseOut(event) {
// ...
}
Off-topic: It's these sorts of browser differences that lead me to geneerally recommend using a library like jQuery, Prototype, YUI, Closure, or any of several others. They smooth over differences for you as well as providing lots of handy utility functions.
table.rows[i].onmouseout = "GridRow"; doesn't make a lot of sense, table.rows[i].onmouseout = function(){alert('hello');}; or some other valid script ought to work though.
Why don't you just use jQuery or some other JavaScript framework? This way your code gets more simple.
var i = 0;
$('#some_table tr').each(function() {
if (i % 2 == 0) {
$(this).addClass('GridRow');
$(this).mouseout(function(evt) { /* your GridRow function */ });
} else {
$(this).addClass('GridRowAlt');
$(this).mouseout(function(evt) { /* your GridRowAlt function */ });
}
i++;
})
Sultan
The original question is not to alert "GridRow". I'm quit sure GridRow is a function name. Fortunately each function is a child of window so write window["GridRow"].
I would add a well known bind-event function, because you need it quite often.
var bindEvent=function(elem,evt,func){
if(elem.addEventListener){
elem.addEventListener(evt,func,false);
}
else if(elem.attachEvent){
elem.attachEvent('on'+evt,function(){
func.call(event.srcElement,event);
})
}
};
and then:
bindEvent(table.rows[i],"mouseout",window["GridRow"]);

JS: using 'var me = this' to reference an object instead of using a global array

The example below, is just an example, I know that I don't need an object to show an alert box when user clicks on div blocks, but it's just a simple example to explain a situation that frequently happens when writing JS code.
In the example below I use a globally visible array of objects to keep a reference to each new created HelloObject, in this way events called when clicking on a div block can use the reference in the arry to call the HelloObject's public function hello().
1st have a look at the code:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>Test </title>
<script type="text/javascript">
/*****************************************************
Just a cross browser append event function, don't need to understand this one to answer my question
*****************************************************/
function AppendEvent(html_element, event_name, event_function) {if(html_element) {if(html_element.attachEvent) html_element.attachEvent("on" + event_name, event_function); else if(html_element.addEventListener) html_element.addEventListener(event_name, event_function, false); }}
/******************************************************
Just a test object
******************************************************/
var helloobjs = [];
var HelloObject = function HelloObject(div_container)
{
//Adding this object in helloobjs array
var id = helloobjs.length; helloobjs[id] = this;
//Appending click event to show the hello window
AppendEvent(div_container, 'click', function()
{
helloobjs[id].hello(); //THIS WORKS!
});
/***************************************************/
this.hello = function() { alert('hello'); }
}
</script>
</head><body>
<div id="one">click me</div>
<div id="two">click me</div>
<script type="text/javascript">
var t = new HelloObject(document.getElementById('one'));
var t = new HelloObject(document.getElementById('two'));
</script>
</body></html>
In order to achive the same result I could simply replace the code
//Appending click event to show the hello window
AppendEvent(div_container, 'click', function()
{
helloobjs[id].hello(); //THIS WORKS!
});
with this code:
//Appending click event to show the hello window
var me = this;
AppendEvent(div_container, 'click', function()
{
me.hello(); //THIS WORKS TOO AND THE GLOBAL helloobjs ARRAY BECOMES SUPEFLOUS!
});
thus would make the helloobjs array superflous.
My question is: does this 2nd option in your opinion create memoy leaks on IE or strange cicular references that might lead to browsers going slow or to break???
I don't know how to explain, but coming from a background as a C/C++ coder, doing in this 2nd way sounds like a some sort of circular reference that might break memory at some point.
I also read on internet about the IE closures memory leak issue http://jibbering.com/faq/faq_notes/closures.html (I don't know if it was fixed in IE7 and if yes, I hope it does not come out again in IE8).
Thanks
Aside:
var HelloObject = function HelloObject(div_container)
In general try not to use named inline function expressions. It doesn't usually get you anything and there are serious problems with them in JScript (IE). Either use a function HelloObject() { ... } statement, or a var HelloObject= function() { ... }; anonymous expression.
does this 2nd option in your opinion create memoy leaks on IE
‘Create’? No, your existing code already had leaks on IE.
The listener you applied to the click event has a closure, keeping a reference to the parent function scope. In both examples, the div_container object is in that scope, so you've got a circular reference:
div -> onclick ->
listener function -> parent scope ->
parent function -> reference to div
A reference loop containing a mixture of native JS objects and host objects (such as the div, a DOM Node) is what causes the memory leaks in IE6-7.
To stop this happening, you can pull the click function definition out of the parent:
function HelloObject(div_container) {
AppendEvent(div_container, 'click', HelloObject_hello);
}
function HelloObject_hello() {
alert('hello');
}
Now there is no closure, the div_container is not remembered, and there is no loop/leak. However, the hello function is just a function, with no idea to which div_container it belongs (other than by looking at the event/this it gets on click).
More typically you do need to remember the div, or, if you're doing things in an objecty way, the this:
function HelloObject(element) {
this.element= element;
AppendEvent(element, 'click', this.hello.bind(this));
}
HelloObject.prototype.hello= function() {
alert('Hello, you clicked on a '+this.element);
};
(About function.bind.)
Which of course brings back the reference loop:
element -> onclick
bound hello function -> bound this
Helloer instance -> 'element' member
reference to element
Do you really care about this kind of refloop? Maybe not. It only really affects IE6-7; it's bad in IE6 as the memory isn't given back until you quit the browser, but then there's a growing school of thought that says anyone still using IE6 deserves everything they get.
On IE7, the memory leaks pile up until you leave the page, so it only matters for very long-running pages where you're throwing away old HelloObjects and binding new ones repeatedly. (In that case, a linear Array of HelloObjects that doesn't discard old values would also be a memory leak until page unload, in itself.)
If you do care, because you're working for some dingy corporate that runs IE6 and no-one ever closes their browsers, then (a) my condolences, and (b) yes, you will indeed have to implement something like the lookup object you had, to act as a decoupling layer between the DOM Nodes and the Function object you're using as an event listener.
Some frameworks have their own decoupling used for events. For example, jQuery attaches the event handler functions to a data lookup, indexed by an id that it drops into each Element object; this gives it decoupling for free, though it does have problems of its own...
If you're using plain JavaScript, here's some example helper code.
// Event binding with IE compatibility, and decoupling layer to prevent IE6-7
// memory leaks
//
// Assumes flag IE67 pre-set through eg. conditional comments
//
function EventTarget_addEventListener(target, event, listener) {
if ('addEventListener' in target) {
target.addEventListener(event, listener, false);
} else if ('attachEvent' in target) {
if (IE67)
listener= listener.decouple();
target.attachEvent('on'+event, listener);
}
}
Function.decouple_bucket= {};
Function.decouple_factory= function() {
function decoupled() {
return Function.decouple_bucket[decoupled.decouple_id].apply(this, arguments);
}
return decoupled;
};
Function.prototype.decouple_id= null;
Function.prototype.decouple= function() {
var decoupled= Function.decouple_factory();
do {
var id= Math.floor(Math.random()*(Math.pow(2, 40)));
} while (id in Function.decouple_bucket);
decoupled.decouple_id= id;
Function.decouple_bucket[id]= this;
return decoupled;
};
Function.prototype.release= function() {
delete _decouple_bucket[this.decouple_id];
};
if (IE67) {
EventTarget_addEventListener(window, 'unload', function() {
Function.decouple_bucket.length= 0;
});
}
// Add ECMA262-5 method binding if not supported natively
//
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
if (arguments.length<=1) {
return function() {
return that.apply(owner, arguments);
};
} else {
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
};
}
};
}
With all that tedious plumbing out of the way, you can then just say:
function HelloObject(element) {
this.element= element;
EventTarget_addEventListener(element, 'click', this.hello.bind(this));
}
HelloObject.prototype.hello= function() {
alert('Hello, you clicked on a '+this.element);
};
that might lead to browsers going slow or to break???
No, it's only really memory leaks (and then mostly in IE) that we're worried about when talking about refloops.
The answer is in the body of the question. It seems weird to you because of your C++ background. The 2nd option is the Javascript-ish way of doing it.

Categories

Resources