How to change onclick of element onclick without immediately running? - javascript

When I click a button I want to change the onclick of the html tag.
Here's my code:
JavaScript:
function reset() {
//some code 1
}
function hit() {
//some code 2
document.getElementById("htmlId").onclick = reset;
}
HTML:
<!DOCTYPE html>
<html id = "htmlId">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width height=device-height">
</head>
<body>
<button id = "btn1" onclick = "hit();" class = "btn">Hit</button>
<script src="script.js">
</script>
</body>
</html>
When I click the button I expect for some code 2 to execute and on my next click for some code 1 to execute. But when I click both some code 1 and some code 2 execute immediately.
If you add an empty onclick before it, reset(); still runs.
JavaScript:
function empty() {
}
function hit() {
//some code 2
document.getElementById("htmlId").onclick = empty;
document.getElementById("htmlId").onclick = reset;
}
How can I fix this?

There are a few things going on here that are interesting to address:
You have JavaScript in HTML attributes which is no longer considered good practice.
Dealing with the whole HTML element and a button inside of that element that you want to manage will lead to bubble and capture issues
You want to change the listeners on elements but you are using the onclick property while modern practice is to use addEventListener and removeEventListener.
Here are my suggestions:
Put all of the JavaScript listeners in the JS file; your HTML should be free of JavaScript.
Use addEventListener instead of assigning to onclick.
Because event listener adding and removing can be tricky, consider using a variable that records whether the button has been pressed. Then your html click listener does a reset only if in that state.
So something like (code is not tested, but should get you started):
let shouldReset = false;
document.getElementById("btnId").addEventListener('click', (e) => {
shouldReset = true;
e.stopPropagation(); // so the big HTML element doesn't get it
}
document.getElementById("htmlId").addEventListener('click', () => {
if (shouldReset) {
reset();
}
}
So clicks on the html element do nothing unless the button has been first clicked. Hope that helps.

Simply create another function, and attach that once the first function has executed:
function hit() {
// you initial hit() function's code here
document.getElementById("btn1").onclick = '';
setTimeout(function(){
document.getElementById("btn1").addEventListener('click', function() {
/* define the next function */
})
}), 1);
};
or to better apply to your case :
function reset() { ... }
function hit() {
// you initial hit() function's code here
document.getElementById("btn1").onclick = '';
setTimeout(function(){
document.getElementById("btn1").addEventListener('click', reset)
}, 1);
};
This can be improved in many ways, but for the scope of your question it should do just fine.

Related

How to add two onclick method in one button? [duplicate]

Is there any way to use the onclick html attribute to call more than one JavaScript function?
onclick="doSomething();doSomethingElse();"
But really, you're better off not using onclick at all and attaching the event handler to the DOM node through your Javascript code. This is known as unobtrusive javascript.
A link with 1 function defined
Click me To fire some functions
Firing multiple functions from someFunc()
function someFunc() {
showAlert();
validate();
anotherFunction();
YetAnotherFunction();
}
This is the code required if you're using only JavaScript and not jQuery
var el = document.getElementById("id");
el.addEventListener("click", function(){alert("click1 triggered")}, false);
el.addEventListener("click", function(){alert("click2 triggered")}, false);
I would use the element.addEventListener method to link it to a function. From that function you can call multiple functions.
The advantage I see in binding an event to a single function and then calling multiple functions is that you can perform some error checking, have some if else statements so that some functions only get called if certain criteria are met.
Sure, simply bind multiple listeners to it.
Short cutting with jQuery
$("#id").bind("click", function() {
alert("Event 1");
});
$(".foo").bind("click", function() {
alert("Foo class");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo" id="id">Click</div>
ES6 React
<MenuItem
onClick={() => {
this.props.toggleTheme();
this.handleMenuClose();
}}
>
var btn = document.querySelector('#twofuns');
btn.addEventListener('click',method1);
btn.addEventListener('click',method2);
function method2(){
console.log("Method 2");
}
function method1(){
console.log("Method 1");
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Pramod Kharade-Javascript</title>
</head>
<body>
<button id="twofuns">Click Me!</button>
</body>
</html>
You can achieve/call one event with one or more methods.
You can add multiple only by code even if you have the second onclick atribute in the html it gets ignored, and click2 triggered never gets printed, you could add one on action the mousedown but that is just an workaround.
So the best to do is add them by code as in:
var element = document.getElementById("multiple_onclicks");
element.addEventListener("click", function(){console.log("click3 triggered")}, false);
element.addEventListener("click", function(){console.log("click4 triggered")}, false);
<button id="multiple_onclicks" onclick='console.log("click1 triggered");' onclick='console.log("click2 triggered");' onmousedown='console.log("click mousedown triggered");' > Click me</button>
You need to take care as the events can pile up, and if you would add many events you can loose count of the order they are ran.
One addition, for maintainable JavaScript is using a named function.
This is the example of the anonymous function:
var el = document.getElementById('id');
// example using an anonymous function (not recommended):
el.addEventListener('click', function() { alert('hello world'); });
el.addEventListener('click', function() { alert('another event') });
But imagine you have a couple of them attached to that same element and want to remove one of them. It is not possible to remove a single anonymous function from that event listener.
Instead, you can use named functions:
var el = document.getElementById('id');
// create named functions:
function alertFirst() { alert('hello world'); };
function alertSecond() { alert('hello world'); };
// assign functions to the event listeners (recommended):
el.addEventListener('click', alertFirst);
el.addEventListener('click', alertSecond);
// then you could remove either one of the functions using:
el.removeEventListener('click', alertFirst);
This also keeps your code a lot easier to read and maintain. Especially if your function is larger.
React Functional components
<Button
onClick={() => {
cancelAppointment();
handlerModal();
}}
>
Cancel
</Button>
const callDouble = () =>{
increaseHandler();
addToBasket();
}
<button onClick={callDouble} > Click </button>
It's worked for me, you can call multiple functions in a single function. then call that single function.
Here is another answer that attaches the click event to the DOM node in a .js file. It has a function, callAll, that is used to call each function:
const btn = document.querySelector('.btn');
const callAll =
(...fns) =>
(...args) =>
fns.forEach(fn => fn?.(...args));
function logHello() {
console.log('hello');
}
function logBye() {
console.log('bye');
}
btn.addEventListener('click',
callAll(logHello, logBye)
);
<button type="button" class="btn">
Click me
</button>
You can compose all the functions into one and call them.Libraries like Ramdajs has a function to compose multiple functions into one.
Click me To fire some functions
or you can put the composition as a seperate function in js file and call it
const newFunction = R.compose(fn1,fn2,fn3);
Click me To fire some functions
This is alternative of brad anser - you can use comma as follows
onclick="funA(), funB(), ..."
however is better to NOT use this approach - for small projects you can use onclick only in case of one function calling (more: updated unobtrusive javascript).
function funA() {
console.log('A');
}
function funB(clickedElement) {
console.log('B: ' + clickedElement.innerText);
}
function funC(cilckEvent) {
console.log('C: ' + cilckEvent.timeStamp);
}
div {cursor:pointer}
<div onclick="funA(), funB(this), funC(event)">Click me</div>

Function is not accessible after onload event. "TypeError: {someFunctionName} is not a function"

I have written a small and simple slider with Javascript. Because I want to be sure that the slider works when I load the javascript in the footer of the page. I added an onload event and copied the whole slider application inside the event. In the HTML I unfortunately have an inline onclick element in a tag. But since I have the code inside the onload scope the onclick doesn't work anymore. My idea is not to bind the event inline in the html but directly in the javascript. That should work. But I am also interested if it is possible to do it with the inline onclick.
Question What do I have to do so that the onclick element addresses the corresponding function within the onclick function?
document.querySelector('body').onload = function() {
function init() {
// ...
}
const f2 = function() {
// ...
}
init();
/* that will work */
const anchorPrev = document.querySelector('.prev');
anchorPrev.addEventListener('click', () => {
console.log('prev');
});
/* My question */
function next() {
console.log('next')
}
};
a {
cursor: pointer;
}
<body>
<a class="next" onclick="next()">next (I'm curious to know if it works!?)</a><br/>
<a class="prev">prev (Will work)</a>
</body>
Two issues:
It's better to wait for the DOMContentLoaded event on the window object.
You're defining the function within the scope of the function, so it's not globally accessible. This means that the onclick can't see the function. Use a let variable, then set the function inside the listener callback like this:
<button onclick="log()">click me</button>
<script>
let log;
window.addEventListener('DOMContentLoaded', () => {
console.log('loaded');
log = () => console.log('clicked');
});
</script>
You can add that the onload event = function next()
JavaSript code:
document.querySelector('body').onload = function() {
const a = document.querySelector('a')
a.onclick = function next() {
event.preventDefault()
console.log('next')
}
};

Javascript Execute onclick event stored in variable

I've created the function below to identify an onclick event which is dynamically generated with each page load. I'm able to get the onclick event into a variable (developer console output shown below). I want to execute that onclick event but can't find a good way of doing that. Any assistance is appreciated.
"ƒ onclick(event) {
mstrmojo.dom.captureDomEvent('*lK1129*kWA92AF1C396244F28902B3171F9642E57*x1*t1530820506700','click', self, event)
}"
function applyAll() {
//Get the self Link to click it
var linkBys = document.getElementsByClassName("mstrmojo-DocTextfield-valueNode");
// loop through each result
for(y = 0;y < linkBys.length;y++){
// retrieve the current result from the variable
var linkBy = linkBys[y];
// check the condition that tells me this is the one I'm looking for
if(linkBy.innerText.indexOf("link") !== -1){
// Find the right class
var idy = document.getElementsByClassName("mstrmojo-DocTextfield-valueNode")[y].onclick;
console.log(idy);
}
}
}
If the property 'onclick' is defined as a function, you can just run it as a function.
var idy = document.getElementsByClassName("")[y].onclick();
You could also handle it another way:
var idy = document.getElementsByClassName("")[y].onclick;
idy();
onclick is not an event, it's a function which gets executed when element is clicked. If you want to simulate click you can do element.click()
If you used:
element.addEventListener('click',()=>...);
instead of:
element.onclick=()=>...
then all you have to do is:
document.getElementsByClassName("mstrmojo-DocTextfield-valueNode")[y].dispatchEvent(new Event('click'));
You can call the function returned , adding parens:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
function foo() {
var idy = document.getElementsByClassName("mstrmojo-DocTextfield-valueNode")[0].onclick;
console.log(idy);
idy();//like so
}
function alertMe() {
alert('Hello');
}
</script>
</head>
<body>
<button id="btn" class="mstrmojo-DocTextfield-valueNode" onclick="alertMe();">No click</button>
<button id="btn2" onclick="foo()">Click me</button>
</body>
</html>

How can I observe changes to my DOM and react to them with jQuery?

I have this function where I toggle a class on click, but also append HTML to an element, still based on that click.
The problem is that now, I'm not listening to any DOM changes at all, so, once I do my first click, yup, my content will be added, but if I click once again - the content gets added again, because as far as this instance of jQuery is aware, the element is not there.
Here's my code:
(function($) {
"use strict";
var closePluginsList = $('#go-back-to-setup-all');
var wrapper = $('.dynamic-container');
$('#install-selected-plugins, #go-back-to-setup-all').on('click', function(event) {
$('.setup-theme-container').toggleClass('plugins-list-enabled');
if ( !wrapper.has('.plugins-container') ){
var markup = generate_plugins_list_markup();
wrapper.append(markup);
} else {
$('.plugins-container').hide();
}
});
//Below here, there's a lot of code that gets put into the markup variable. It's just generating the HTML I'm adding.
})(jQuery);
Someone suggested using data attributes, but I've no idea how to make them work in this situation.
Any ideas?
You could just do something like adding a flag and check for it before adding your markup.
var flag = 0;
$('#install-selected-plugins, #go-back-to-setup-all').on('click', function(event) {
$('.setup-theme-container').toggleClass('plugins-list-enabled');
if ( !wrapper.has('.plugins-container') ){
var markup = generate_plugins_list_markup();
if(flag == 0){
wrapper.append(markup);
flag = 1;
}
} else {
$('.plugins-container').hide();
}
});
If you want to add element once only on click then you should make use of .one() and put logic you want to execute once only in that handler.
Example :
$(document).ready(function(){
$("p").one("click", function(){
//this will get execute once only
$(this).animate({fontSize: "+=6px"});
});
$("p").on("click", function(){
//this get execute multiple times
alert('test');
});
});
html
<p>Click any p element to increase its text size. The event will only trigger once for each p element.</p>

In JavaScript, manually controlling order of event listeners

Assuming that FORM contains INPUT, have the following listeners:
JavaScript
function formFirst(e) { ... }
function formLast(e) { ... }
function inputFirst(e) { ... }
function inputLast(e) { ... }
function middle(e) { ... }
document.getElementById('form').addEventListener('change',formFirst,true);
document.getElementById('form').addEventListener('change',formLast,false);
document.getElementById('input').addEventListener('change',inputFirst,true);
document.getElementById('input').addEventListener('change',inputLast,false);
Desired order of firing
formFirst() // normal - outer element, useCapture = true
inputFirst() // normal - triggering element, declared first
middle() // -- how to do this?
inputLast() // normal - triggering element, declared second
formLast() // normal - outer element, useCapture = false
Nature of problem and attempted solutions
Own code at FORM level, formFirst, formLast and middle, but have no access to INPUT code, inputFirst and inputLast - although could add own listeners on the INPUT.
Attempt 1 modify formFirst() to create and dispatch a new change Event (would be ignored within formFirst) that would call inputFirst(), but have no way of stopping propagation to prevent inputLast() being called subsequently.
Attempt 2 add middle added as listener to INPUT, but cannot guarantee firing order of two listeners of same type and same useCapture.
Premise of Attempt 2 is incorrect - firing order is determined by declaration order within the target Element.
Here are the rules
non-target Element triggers with useCapture=false, starting at the outermost Element and working toward the target Element
a) if more than one useCapture=true triggers for same element, then order of declaration.
at target Element, order of declaration, regardless of useCapture
non-target Element triggers with useCapture=false, starting at the innermost Element and working away from the target Element
a) if more than one useCapture=false triggers for same Element, then order of declaration.
I think that this answers just your question. feel free to comment\contact me for more info.
----- edit ------
OK, I just played with it a little as promised, and I found a very simple solution:
<script type="text/javascript">
function formFirst(e) { alert(1); }
function formLast(e) { alert(5); }
function inputFirst(e) { alert(2); }
function inputLast(e) { alert(4); }
function middle(e) { alert(3); }
function init(){
document.getElementById('form').addEventListener('change',formFirst,true);
document.getElementById('form').addEventListener('change',formLast,false);
document.getElementById('input').addEventListener('change',inputFirst,true);
document.getElementById('input').addEventListener('change',middle,true);
/*** alternative to last tow lines
document.getElementById('input').addEventListener('change',function(){inputFirst();middle();},true);
**/
document.getElementById('input').addEventListener('change',inputLast,false);
}
</script>
<body onload="init();">
<form id="form">
<input type="text" id="input" /> <br />
</form>
</body>
notice:
I put the addEventListener part into an init function, so I can call it after the page is loaded and the element are already exist.
I have run this just on chrome. So I don't want to guarantee you things about other browsers.
An alternative is writing the event handling on your own. here is an example for that. relaying on this article.
<script type="text/javascript">
function formFirst(e) { alert(1); }
function formLast(e) { alert(5); }
function inputFirst(e) { alert(2); }
function inputLast(e) { alert(4); }
function middle(e) { alert(3); }
function init(){
//create event
myHandler = new Event();
//add handler
myHandler.addHandler(formFirst);
myHandler.addHandler(inputFirst);
myHandler.addHandler(middle);
myHandler.addHandler(inputLast);
myHandler.addHandler(formLast);
//regiser one listener on some object
document.getElementById('input').addEventListener('change',function(){myHandler.execute();},true);
}
function Event(){
this.eventHandlers = new Array();
}
Event.prototype.addHandler = function(eventHandler){
this.eventHandlers.push(eventHandler);
}
Event.prototype.execute = function(){
for(var i = 0; i < this.eventHandlers.length; i++){
this.eventHandlers[i]();
}
}
</script>
<body onload="init();">
<form id="form">
<input type="text" id="input" /> <br />
</form>
</body>

Categories