Change the button text/value on click with javascript - javascript

My goal is to include Element.ID within function and then, fetch their value or text. It is important to reduce code lines as well because there are many others buttons with the same rule.
So, I tried the below code and many others to get the appropriate results.
How do I fix it?
var el = document.getElementById("p1");
var id = document.getElementById("p1").id;
el.addEventListener("click", modifyText(id), false);
function modifyText(e) {
var x = e.value;
if (x < 40) {
e.value = 1;
}
};
<input id="p1" type="button" class="button" value=0>
<input id="pn" type="button" class="button" value=0>

Well, the second argument to .addEventListener() has to be a function reference, not "loose" code to execute. So, if you want to call another function and pass it an argument, the line should be:
el.addEventListener("click", function(){modifyText(id)}, false);
Now, you are making quite a bit out of the element's id, but you really only need the id to get your initial reference to the element. Once you've got that, you can just work with it.
You've got a lot of unnecessary code here. Also, I'm assuming (perhaps incorrectly) that you want both buttons to have the same click behavior, so that's what I'm proceeding with.
// You only need to get a reference to the element in question
var el1 = document.getElementById("p1");
var el2 = document.getElementById("pn");
// Set up each button to use the same click callback function
// The second argument needs to be a function reference
el1.addEventListener("click", modifyText);
el2.addEventListener("click", modifyText);
function modifyText(){
// When inside of an event handler, "this" refers to the element
// that triggered the event.
if (this.value < 40 ) {
this.value = 1;
}
}
<input id = "p1" type="button" class="button" value=0>
<input id = "pn" type="button" class="button" value=0>

Event listener callbacks tend to be executed with the execution context of the element (unless otherwise modified, or using Arrow functions). This means you can just use this keyword to refer to the element. So inside the callback you could use this.value / this.innerText (depending on type of element)
function modifyText() {
var x = this.value;
if ( x < 40 ) {
this.value = 1;
}
}
Also the way you called addEventListener was wrong.
.addEventListener("click", modifyText(id), false);
This will execute modifyText immediately and use the return value of the function as the callback. And since your function doesnt return anything nothing is set as the callback.
If you wanted to pass a variable to an event callback you would do it like the following
el.addEventListener("click", modifyText.bind(el,yourValue),false);
You would then need to modify the function definition
function modifyText(passedValue,event) {
}

Related

'this' in callback bound as closure via apply becoming undefined if called in specific order

I have a JS function where I try to use this which is supposed to be the button clicked to trigger the function:
function myAction( greeting ) { console.log( greeting + ", the button is :" + this ); this.remove(); }
I then have several buttons on my page, let's say two (this really even happens with only two buttons on the page; I've tested it):
<button id="button-one" class="click-me">Click Me</button>
<button id="button-two" class="click-me">Click Me</button>
From my experience, when I want to use event listener callbacks which require the use of the concerned element as this plus other function arguments; if we have several buttons sharing for example the same class; the best way to reach this is to bind that callback as a closure (to capture the iterated element as this + apply (to deliver the concerned this element):
var myButtons = document.getElementsByClassName( "click-me" );
var amountOfButtons = myButtons.length;
for ( var i = 0; i < amountOfButtons; i++ ) {
myButtons[i].addEventListener( "click", (
function(i) {
return function() { myAction.apply( myButtons[i], ["hello"] ); };
}(i))
);
}
I never had any problem with this approach; it always worked. Is it actually the wrong approach?
Because, what I'm facing now is that, when I click on #button-one, and then click on #button-two, this actually becomes undefined in strict mode and the window in non-strict mode; on the second click. When I do it the other way around, this always sticks to be the accordingly clicked button. It somehow seems to update the myButtons collection, such that when the first button gets removed via the first call, the 1 index becomes unavailable in the collection, as there's only one remaining button; the other one being removed. At least that's my theory. But I've never faced this issue before in similar code cases? How shall I code what I want to happen then?
EDIT
Thanks to all of you; and special thanks to #Nenad Vracar, as his solution led to the minimum needed edits which perfectly work, by simply changing:
var myButtons = document.getElementsByClassName( "click-me" );
var amountOfButtons = myButtons.length;
for ( var i = 0; i < amountOfButtons; i++ ) {
myButtons[i].addEventListener( "click", (
function(i) {
return function() { myAction.apply( myButtons[i], ["hello"] ); };
}(i))
);
}
To:
var myButtons = document.getElementsByClassName( "click-me" );
var amountOfButtons = myButtons.length;
for ( var i = 0; i < amountOfButtons; i++ ) {
myButtons[i].addEventListener( "click", function() {
myAction.apply( this, ["hello"] );
});
}
Simple smart solution, I could literally not see the wood for the trees! thanks again!
Note. Event delegation was not really an option here, as the DOM structure of the page / the elements needing the attachment is somewhat complicated, and I generally prefer to attach the function to the elements needing it. Also, I prefer to not change the code of the function to be a closure itself, to not make changes to the function code itself. Hence the reason why I've decided to use this solution.
You could use slightly different approach where you use querySelectorAll and then you can use forEach loop on that. And inside the click event handler you don't need that extra closure and this inside the myAction will be the this inside the click event handler since you passed it as thisArg parameter to apply.
function myAction(greeting) {
console.log(greeting + ", the button is :" + this.textContent);
this.remove();
}
document
.querySelectorAll(".click-me")
.forEach(function(btn) {
btn.addEventListener('click', function() {
myAction.apply(this, ['Hello'])
})
})
<button id="button-one" class="click-me">One</button>
<button id="button-two" class="click-me">Two</button>
Your approach right now seems very complex. Here are two suggestions to simplify the process.
Higher order function event handler
If you re-define myAction as a function that returns the event listener, you can pass arguments directly. Therefore, there would be no need to wrap it in another function when assigning the handler:
function myAction( greeting ) { //take input
return function() { //return the function to act as event listener
console.log( greeting + ", the button is :" + this ); this.remove();
}
}
var myButtons = document.getElementsByClassName( "click-me" );
var amountOfButtons = myButtons.length;
for ( var i = 0; i < amountOfButtons; i++ ) {
//pass a variable to be used in the listener
myButtons[i].addEventListener( "click", myAction( "hello" ) );
}
<button id="button-one" class="click-me">Click Me</button>
<button id="button-two" class="click-me">Click Me</button>
Use event delegation
This is the preferred approach. Instead of adding and event listener to each of the elements, you can add a single event listener to a common ancestor in the DOM tree. Then you only need one function.
Worth noting that the event listener will receive events from all children, so it needs to filter out irrelevant ones. You can make a simple wrapper to check if the target target is correct, then execute the decorated handler:
function myAction( greeting ) {
return function(event) { //take an event
var el = event.target; //extract the element from it
console.log( greeting + ", the button is :" + el ); el.remove(); //use the element
}
}
/*
* Convert a handler to a delegate to only work on a sub-section of elements
* #param {string} filterSelector - the handler will only be triggered
* for elements matching this selector
* #param {Function} handler - the handler to wrap
*
* #return {Function} a function to serve as a delegated event handler
*/
function delegate( filterSelector, handler ) {
return function ( event ) {
//only fire for explicitly desired targets
if (event.target.matches( filterSelector ) ) {
//forward call to the handler
handler.apply( this, arguments );
}
}
}
var delegatedHandler = delegate( ".click-me", myAction( "hello" ) );
document.body //attach higher up in the DOM
.addEventListener( "click", delegatedHandler );
<button id="button-one" class="click-me">Click Me</button>
<button id="button-one" class="dummy">I should stay</button>
<button id="button-two" class="click-me">Click Me</button>
See Element#matches() for potential polyfills if you're working with older browsers.
See also:
What is DOM Event delegation?
Event binding on dynamically created elements?

pass through current target name to function

I'd like to dynamically create event listeners for multiple buttons, and subsequently, show a particular frame label depending on the button clicked, but I'm unsure what to pass through (FYI, this is will be used for HTML5 canvas in Flash CC, but principally the same should apply to a web page for showing divs etc). I currently have this:
var butTotal = 4;
var selfHome = this;
function createListeners () {
for (var i=0; i<butTotal; i++) {
selfHome["btn" + i].addEventListener('click', openPop);
}
}
function openPop () {
alert("test");
selfHome.gotoAndPlay("pop"+event.currentTarget.name.substr(3));
}
createListeners();
It creates the listeners fine, but I don't really know where to start with passing through the current button instance name to tell it which frame label to gotoAndPlay.
Based on the code that you have, I'd simply change the .addEventListener() to call a generic function (rather than openPop, directly), and pass it the reference to the button. So, this:
selfHome["btn" + i].addEventListener('click', openPop);
. . . would become this:
selfHome["btn" + i].addEventListener('click', function() {
openPop(this);
});
At that point, you would then have to update openPop to accept a parameter for the reference to the element that triggered it . . . something like:
function openPop (currentButton) {
At that point, you could reference the clicked button, by using currentButton in the openPop logic.
I'm not sure I totally understand your question. However if you just need to pass the button instance (in you case "selfHome["btn" + i]") you could call an anonymous function in your event handler which calls openPop() with the button instance as an arugment. Would this work for you?
var butTotal = 4;
var selfHome = this;
function createListeners () {
for (var i=0; i<butTotal; i++) {
var currentBtn = selfHome["btn" + i];
currentBtn.addEventListener('click', function(){openPop(currentBtn);} );
}
}
function openPop (btn) {
alert("test");
selfHome.gotoAndPlay(/*use button instance 'btn' to find frame*/);
}
createListeners();
When the event is triggered the this keyword inside the handler function is set to the element is firing the event EventTarget.addEventListener on MDN. If the button have the data needed to be retrieved just get it from the this keyword:
function openPop (btn) {
alert(this.name);
/* ... */
}
It looks like you expect it to contain the function gotoAndPlay() as well as the btn elements (which contain both an ID (of btn[number]) and a name with something special at substr(3) (I assume the same as the id). If those things were all true, it should work in chrome... in other browsers you'll need to add event to the openPop() method signature.
function openPop (event) {
alert("test");
selfHome.gotoAndPlay("pop"+event.currentTarget.name.substr(3));
}
I believe this is what you are looking for and adding that one word should fix your problem (assuming some things about your dom and what selfHome contains):
JSFiddle
You could also leave out the event from openPop() and replace event.currentTarget with this:
function openPop () {
alert("test");
selfHome.gotoAndPlay("pop"+this.name.substr(3));
}
JSFiddle

Javascript - Function to use onclick?

I want to create a function and then use with onclick method, for example:
one = document.getElementById("oneID");
then instead of writing function for each onclick():
one.onclick = function(x) {
tempStack.push(parseFloat(one.value));
viewTemp.value += one.value;
}
I want to use a single function:
one.click = input(one);
but I'm not sure how to do it in the correct way for example the below I tried, doesn't work:
var input = function(x) {
tempStack.push(parseFloat(x.value));
viewTemp.value += x.value;
}
Lastly, no external JavaScript libraries to aid this question, vanilla JavaScript.
You'll need to pass a function as a reference, not call it:
one.onclick = input;
In this case you won't be able to pass an argument, but you can use this as a reference for the DOM element on which event is fired:
function input() {
tempStack.push(parseFloat(this.value));
viewTemp.value += this.value;
}
Here's a method with using JavaScript's .addEventListener(), as a previous answer mentioned, using this to pass through the DOM Node Element to use within the inputFunction.
<input type="text" value="64.23" id="bt" />
<script>
function inputFunction( x ) {
console.log( x.value ); //Console Logs 64.23
}
var bt = document.getElementById("bt");
bt.addEventListener( 'click', function(){ inputFunction( this )}, false );
</script>
Fiddle: http://jsfiddle.net/Lhq6t/
Think about functions as a normal objects, so the way is:
function input (event) {
// Process the event...
// event is my event object
// this is the object which trigger the event
// event.target is my button
}
on.onclick = input;
You must assign the input function as a normal variable.
The function input will receive an event object as parameter. Also you can refer to the button clicked with this.
Maybe the mozilla developer network or the real w3c site would explain it better.
Your requirement can be achieved by following:
Add this method in your script tag:
function input(x) {
/*tempStack.push(parseFloat(x.value));
viewTemp.value += x.value;*/
alert(x.id);
}
And then call this method onClick event of your buttons / anchors like:
<input type="button" id="oneID" value="oneID" onClick="input(this);"/>
<input type="button" id="twoID" value="twoID" onClick="input(this);"/>
threeID
See working example: http://jsfiddle.net/Avd5U/1/
ok, so just create a function with a parameter in it like:
function setValue(input){
tempStack.push(parseFloat(input.value));
viewTemp.value += input.value;
}
and then call the function on the click of that element like:
var one = document.getElementById("oneID");
one.click = setValue(one);
Good luck!

How do I pass local variable value from one function to another?

In my script I have 2 functions. First function references to a div element, creates a paragraph element inside div and appends some text to this paragraph element;
In my second function is triggered by onclick event attached to a link element. I want the text in the div to be changed to another text when clicking on the link. I do realize that there are 2 options how to achieve this:
1) declare global variables and use them in my second function;
2) pass the variable value from first function to the second function and manipulkate this value from the second function
But the question is how to do I correctly pass the variable value from first function to second function:
Here is the code:
<a href=''onclick='change();return false;'>Change</a>
<div id='box'></div>
Javascript:
window.onload= function createEl(){
var el = document.createElement('p');
var x = document.getElementById('box');
var text = 'text';
el.appendChild(document.createTextNode(text));
x.appendChild(el);
}
function change(){
x.innerHTML="other text";
}
in general you can write this:
function one(){
var var1 = "hello";
two(var1);
}
function two(x){
alert(x);
}
this will alert "hello".
For what you're doing, I would register my events through code to make it easier to pass a variable. We want to use an argument in the event handling function to pass the data to it.
window.onload = function()
{
// do your normal stuff with creating elements
var anc = document.getElementById('ID of your a element here');
if(anc.attachEvent)
{
//code for ancient IE
anc.attachEvent('onclick', function(){change(x);});
}
else if(anc.addEventListener)
{
//code for modern browsers
anc.addEventListener('click', function(){change(x);});
}
}
function change(elem)
{
elem.innerHTML='other text';
}
Do note that older versions of IE don't recognize addEventListener and use attachEvent instead, as seen in the above if block. Here's the documentation for addEventListener.

Passing elements as variables to function

I'm having an issue with an element object and a jQuery function:
HTML
<label for='state'>State</label>
<input id='state' name='state' type='text' value=''/>
<span class='info'><img class='tick' /><img class='cross' /></span>
JavaScript / jQuery
var state = $("#state");
function validatefield(myelement) {
if (myelement.val().length > 3) {
alert("word");
} else {
alert("sup");
}
}
state.blur(validatefield(state));
state.keyup(validatefield(state));
Nothing happens on page load, even when state has more than 3 chars entered.
Any ideas?
Awesome - learning new stuff ftw
No need for arguments at all, the event handler is bound to the element so that you can use the this keyword inside the function:
var state = $("#state");
function validatefield(event) {
if (this.value.length > 3) { // <-- use `this.value` instead
alert("word");
} else {
alert("sup");
}
}
state.blur(validatefield);
state.keyup(validatefield);
The way you're attempting it will actually call the function and use its return value as the event handler, which is why nothing was happening:
// validatefield(state) is executed immediately and the return value, "undefined"
// is passed as the first argument to state.blur()
state.blur(validatefield(state));
To fix other situations like this where the this keyword is not available, you should use an anonymous function:
state.blur(function () { validatefield(state) });
Wrap the function calls in anonymous functions.
$(document).ready(function(){
var state = $("#state");
state.blur(function() {validatefield(state)});
state.keyup(function() {validatefield(state)});
});
http://jsfiddle.net/eW8E8/1/
You should use an anonymous function as jQuery event handler, instead of
state.keyup(validatefield(state));
use
state.keyup(function() {
validatefield(state);
});
Shouldnt it be:
if(myelement.value.length > 3) {
state.keyup(validatefield.call(this, state))
should also work (see http://ejohn.org/apps/learn/#26)

Categories