Basically, when a button is pressed I want an argument to be supplied with it but this does not work:
var button = document.getElementById("button");
button.onClick = doThis(arg);
but this does work (without arguments):
var button = document.getElementById("button");
button.onClick = doThis;
The reason why the first example doesn't work it because the function automatically runs without waiting for the click.
How do I supply arguments onClick?
First, note that it's onclick, not onClick. Both work on major browsers, but the former is the correct capitalization. (See here and here in the HTML specification, including the code example.)
You have a couple of choices:
Use Function#bind:
button.onclick = doThis.bind(button, arg);
Function#bind creates a new function that, when called, will call the original with a specific this value (the first argument, button in our case) and any arguments you give it (followed by any arguments the new function is called with).
Use a wraper function:
button.onclick = function() { doThis(arg); };
within the above, though, this in doThis will not be the button. If you want it to be, you can use Function#call:
button.onclick = function() { doThis.call(button, arg); };
// or
button.onclick = function() { doThis.call(this, arg); };
Function#call calls a function, specifying the this value to use, along with the arguments to pass to it.
You could do it like this using an anonymous function.
document.getElementById("button").onClick = function() { doThis(arg); };
You can do it well using addEventListener in JavaScript.
HTML5 data-attributes and DOMStringMap can be utilized to extend it further.
Below code snippet should give you fair idea of using arguments with any HTMLDomEvents.
el.addEventListener('click', function(e) {
performAction(e, this);
});
var elContainer = document.querySelector('#myDiv');
var el = document.querySelector('#searchNow');
el.addEventListener('click', function(e) {
performAction(e, this);
});
function performAction(e, thatObj) {
var str = '' + e.type + '\n';
for (x in thatObj.dataset) {
str += x + ': ' + thatObj.dataset[x] + '\n';
}
console.log(e.type + thatObj.dataset);
elContainer.innerHTML += str;
}
#myDiv {
margin: 5px 0;
padding: 5px;
border: 1px solid #CCC;
}
<div id="myDiv">
My DIV...
<br/>
</div>
<button name='search' id='searchNow' data-propertiesObject='{a: "XYZ", b: "ABC"}'>Search Now!</button>
Related
Why doesn't the callButtonTwo() function work on all buttons? Only button1 reacts to it?
document.querySelector("h1").addEventListener("click", function() {
alert("Working!");
})
var totalButton = document.querySelectorAll(".testSecond").length;
for (var i = 0; i < totalButton; i++) {
document.querySelectorAll(".testSecond")[i].addEventListener("click", function() {
var buttonNow = this.innerHTML;
callButtonOne(buttonNow);
callButtonTwo(buttonNow);
});
}
function callButtonOne() {
alert("I got clicked!");
};
function callButtonTwo() {
var changeRed = document.querySelector(".testSecond");
changeRed.classList.add("red");
setTimeout(function() {
changeRed.classList.remove("red");
}, 300);
};
.red {
background-color: red;
}
<h1 class="testFirst">Hello World!</h1>
<button class="testSecond button1" type="button" name="button">button1</button>
<button class="testSecond button2" type="button" name="button">button2</button>
<button class="testSecond button3" type="button" name="button">button3</button>
<button class="testSecond button4" type="button" name="button">button4</button>
<button class="testSecond button5" type="button" name="button">button5</button>
While, inside your click handler you have var buttonNow = this.innerHTML; which operates on that button, when you call callButtonTwo you say var changeRed = document.querySelector(".testSecond"); which operates on the first button (no matter which button you click).
You need to tell it which button you are dealing with (e.g. by passing this as an argument)
The problem is, that your querySelector in callButtonTwo just finds the first button because all of them have the same class .testSecond.
One possible solution is to add the reference of the button which should be colored red to the callButtonTwo function and use the reference to add the class name.
It would look like this:
function callButtonTwo(button) {
button.classList.add("red");
setTimeout(function() {
button.classList.remove("red");
}, 300);
};
And you would call it like this: callButtonTwo(this);
Here's a working example based on the code you provided.
Inside callButtonTwo you're querying .testSecond again which will always return the first element in the page with that class. If you want to handle each button differently you should pass the button element as a parameter to callButtonTwo, like so:
var totalButton = document.querySelectorAll(".testSecond").length;
for (var i = 0; i < totalButton; i++) {
document.querySelectorAll(".testSecond")[i].addEventListener("click", function() {
var buttonNow = this.innerHTML;
callButtonOne(buttonNow);
callButtonTwo(this); // 'this' in this case is the clicked button element
});
}
[...]
function callButtonTwo(button) {
button.classList.add("red");
setTimeout(function() {
button.classList.remove("red");
}, 300);
};
I would also consider to change the way you're iterating your elements in order to query the DOM less frequently:
// 'querySelectorAll' returns an array which can be directly iterated using its method 'forEach'
document.querySelectorAll(".testSecond").forEach(function(button) {
button.addEventListener("click", function() {
var buttonNow = this.innerHTML;
callButtonOne(buttonNow);
callButtonTwo(this);
});
});
As in the title of the question.
I have many elements, because I have used getElementsByTagName('*').
Then, I have added a click event on every element, and I have used loop for that.
See the code:
HTML
<div id="box">
<span class="box"> span</span><br/>
<span class="box">span 2</span><br/>
<span class="box">span 3</span><br/>
</div>
<div id="result"></div>
Javascript
var element = document.getElementsByTagName('*'),
len = element.length, result = document.getElementById('result'), i, timer;
for (i = 0; i < len; i++) {
element[i].addEventListener('click', fn = function (e) {
clearTimeout(timer);
result.style.display = 'inline';
result.innerHTML = "<pre>" + e.target.innerHTML + "</pre>";
timer = window.setTimeout(function () {
result.style.display = 'none';
}, '2000');
e.target.removeEventListener('click', fn);
});
}
I want to when a user clicks on a specific element, implement the
event once, then removes the event from this element only.
Also, I want to add the function(callback) name to the removeEventListener function automatically, not like this e.target.removeEventListener('click', fn) //fn is the callback name.
the event callback gets called with the context of element, you have added the listener to it, here this would point to element[i],so you can change it like:
element[i].addEventListener('click', function fn(e) {
//your stuff
this.removeEventListener('click', fn);
});
note that if you create fn function this way, it is kind of private in the function body, we used to use arguments.callee which is not a good practice these days, you can not use it in strict mode.
all I am saying is by the time strict mode showed up since:
The 5th edition of ECMAScript (ES5) forbids use of arguments.callee()
in strict mode.
we could do that like this:
element[i].addEventListener('click', function(e) {
//your stuff
this.removeEventListener('click', arguments.callee);
});
but the new alternative is using function's label, for instance if you do:
var myfunc = function func(){
//you have access to the current function using func
//and you can add or remove it to/from something
someThing.removeEventListener('click', func);
};
//but if you want to do it here you can have it using myfunc
someOtherThing.removeEventListener('click', myfunc);
So that's what I mean by:
kind of private in the function body
you have access to that function in the function body using its label func, but out there in the code you don't have it.
Define function before as a variable. http://jsfiddle.net/m8UgC/
var element = document.getElementsByTagName('*'),
len = element.length, result = document.getElementById('result'), i, timer;
var fn = function (e) {
clearTimeout(timer);
result.style.display = 'inline';
result.innerHTML = "<pre>" + e.target.innerHTML + "</pre>";
timer = window.setTimeout(function () {
result.style.display = 'none';
}, '2000');
this.removeEventListener('click', fn);
}
for (i = 0; i < len; i++) {
element[i].addEventListener('click', fn);
}
For a button, I need to set the function this calls, and the value used in that call, at runtime.
I can do this like so:
var myfunction = /* ... */
var myvalue = /* ... */
button.setAttribute ("onclick", myfunction + "('" + myvalue + "')");
If I try instead:
button.setAttribute ("onclick", function () { myfunction(myvalue));
Firebug gives me a
"function statement requires a name"
even though, as far as I'm aware, I'm using a function expression here.
button.onclick = function() {
myfunction(myvalue);
}
Using the proper event registration method would be better though:
button.addEventListener('click', function() {
myfunction(myvalue);
}, false);
You're missing a closing brace.
button.setAttribute ("onclick", function () { myfunction(myvalue));
should be
button.setAttribute ("onclick", function () { myfunction(myvalue); });
But you probably don't want to use setAttribute to set the onclick of a button, try button.onclick = function () { myfunction(myvalue); }; instead
If you are considering pure JavaScript:
function attachEvent(element, event, handler)
{
if (element && element.attachEvent) {
element.attachEvent("on" + event, handler);
}
else if (element && element.addEventListener) {
element.addEventListener(event, handler, false);
}
}
USAGE:
attachEvent(button, "click", myFunction);
Please be noted, IE requires attachEvent and addEventListener doesn't work on IE and works on other browser. So you have to consider both of these functions to make it cross-browser support.
I'm using the CSS3 :target pseudo selector to create in-page navigation without reloading the page. This works really well!
But I have a problem, I need to reset the forms in a page when the page targetted, how can I know if an element is targetted with javascript? Like element.ontarget = function();
Or maybe something like element.ondisplaychange -> element.oncsschange?
BETTER UPDATE:
var hashcache = document.location.hash;
window.onhashchange = function() {
if(hashcache != document.location.hash) {
$(hashcache + ' form input').each(function() {
$(this).val('');
});
hashcache = document.location.hash;
}
}
UPDATE:
$('a[href^="#"]').each(function() {
this.onclick = function() {
href = $(this).attr('href');
if(href != document.location.hash) {
$(href + ' form input').each(function() {
$(this).val('');
});
}
}
});
If you're using JavaScript for the navigation, I'd suggest just adding the check to that. But I'm guessing from your question you're not, that you're instead using plain links with just anchors (e.g., <a href='#target1'>, <a href='#target2'>, ...).
A couple of options:
Use a Timer
In that case, basically what you want to do boils down to receiving an event when the anchor changes. As far as I know, and as far as the people answering this other question on StackOverflow in January knew, you can only do that with a timer. (Edit: But see ide's comment below, there's a new hashchange event we'll be able to use soon!) E.g.:
(function() {
var lastHash = window.location.hash;
setTimeout(function() {
var newHash = window.location.hash;
if (newHash !== lastHash) {
lastHash = newHash;
// Trigger your target change stuff
}
}, 250);
})();
That checks for changes every quarter second. That may not be enough for you, you could lower the 250, but beware running too much and slowing everything else down.
But as you say below, this is inefficient.
Hook the Link's click event
Since you're already using JavaScript on the page, I'd recommend using handlers on your links instead. If you add a class name or something to them (I bet they already have one; I'll us "navlink" below), this is easily set up:
var links, index, link;
links = document.getElementsByTagName('a');
for (index = 0; index < links.length; ++index) {
link = links.item(index);
if ((" " + link.className + " ").indexOf(" navlink ") >= 0) {
hookEvent(link, 'click', clickHandler);
}
}
function clickHandler() {
// `this` will reference the element that was clicked
}
// The 'hook' function:
var hookEvent = (function() {
var elm = document.createElement('a');
function hookEventViaAttach(element, event, handler) {
element.attachEvent("on" + event, handler);
}
function hookEventViaAddListener(element, event, handler) {
element.addEventListener(event, handler, false);
}
function hookEventDOM0(element, event, handler) {
element["on" + event.toLowerCase()] = handler;
}
if (elm.attachEvent) {
return hookEventViaAttach;
}
if (elm.addEventListener) {
return hookEventViaAddListener;
}
// I usually throw a failure here saying not supported, but if you want,
// you can use the DOM0-style stuff.
return hookEventDOM0;
})();
A lot of the complication of the above goes away if you use a library like jQuery, Prototype, YUI, Closure, or any of several others.
For instance, the jQuery version:
$("a.navlink").click(clickHandler);
function clickHandler() {
// `this` will reference the element that was clicked
}
The Prototype version:
$$("a.navlink").invoke('observe', 'click', clickHandler);
function clickHandler() {
// `this` will reference the element that was clicked
}
The onfocus property returns the onFocus event handler code on the current element.
event handling code = element.onfocus
The onblur property returns the onBlur event handler code, if any, that exists on the current element.
element.onblur = function;
Example: http://jsfiddle.net/g105b/cGHF7/
<html>
<head>
<title>onblur event example</title>
<script type="text/javascript">
var elem = null;
function initElement()
{
elem = document.getElementById("foo");
// NOTE: doEvent(); or doEvent(param); will NOT work here.
// Must be a reference to a function name, not a function call.
elem.onblur = doEvent;
};
function doEvent()
{
elem.value = 'Bye-Bye';
alert("onblur Event detected!")
}
</script>
<style type="text/css">
<!--
#foo {
border: solid blue 2px;
}
-->
</style>
</head>
<body onload="initElement()";>
<form>
<input type="text" id="foo" value="Hello!" />
</form>
<p>Click on the above element to give it focus, then click outside the
element.<br /> Reload the page from the NavBar.</p>
</body>
</html>
Maybe youcan just code like this
function hashChangeEvent(){
$(window.location.hash)//do something
}
window.onhashchange = hashChangeEvent;//when hash change
hashChangeEvent();//first load
Is there a best-practice or common way in JavaScript to have class members as event handlers?
Consider the following simple example:
<head>
<script language="javascript" type="text/javascript">
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked;
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
</script>
</head>
<body>
<input type="button" id="btn1" value="Click me" />
<script language="javascript" type="text/javascript">
var btn1counter = new ClickCounter('btn1');
</script>
</body>
The event handler buttonClicked gets called, but the _clickCount member is inaccessible, or this points to some other object.
Any good tips/articles/resources about this kind of problems?
ClickCounter = function(buttonId) {
this._clickCount = 0;
var that = this;
document.getElementById(buttonId).onclick = function(){ that.buttonClicked() };
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
EDIT almost 10 years later, with ES6, arrow functions and class properties
class ClickCounter {
count = 0;
constructor( buttonId ){
document.getElementById(buttonId)
.addEventListener( "click", this.buttonClicked );
}
buttonClicked = e => {
this.count += 1;
console.log(`clicked ${this.count} times`);
}
}
https://codepen.io/anon/pen/zaYvqq
I don't know why Function.prototype.bind wasn't mentioned here yet. So I'll just leave this here ;)
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked.bind(this);
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
A function attached directly to the onclick property will have the execution context's this property pointing at the element.
When you need to an element event to run against a specific instance of an object (a la a delegate in .NET) then you'll need a closure:-
function MyClass() {this.count = 0;}
MyClass.prototype.onclickHandler = function(target)
{
// use target when you need values from the object that had the handler attached
this.count++;
}
MyClass.prototype.attachOnclick = function(elem)
{
var self = this;
elem.onclick = function() {self.onclickHandler(this); }
elem = null; //prevents memleak
}
var o = new MyClass();
o.attachOnclick(document.getElementById('divThing'))
You can use fat-arrow syntax, which binds to the lexical scope of the function
function doIt() {
this.f = () => {
console.log("f called ok");
this.g();
}
this.g = () => {
console.log("g called ok");
}
}
After that you can try
var n = new doIt();
setTimeout(n.f,1000);
You can try it on babel or if your browser supports ES6 on jsFiddle.
Unfortunately the ES6 Class -syntax does not seem to allow creating function lexically binded to this. I personally think it might as well do that. EDIT: There seems to be experimental ES7 feature to allow it.
I like to use unnamed functions, just implemented a navigation Class which handles this correctly:
this.navToggle.addEventListener('click', () => this.toggleNav() );
then this.toggleNav() can be just a function in the Class.
I know I used to call a named function but it can be any code you put in between like this :
this.navToggle.addEventListener('click', () => { [any code] } );
Because of the arrow you pass the this instance and can use it there.
Pawel had a little different convention but I think its better to use functions because the naming conventions for Classes and Methods in it is the way to go :-)