I have a dead simple search form:
<div>
<input id="searchArea" type="text" value="" placeholder="Enter your search terms">
<button id="searchButton">Search</button>
</div>
and a snippet of Javascript controlling it:
function searchFunction() {
console.log("This is a POST request being send to the server");
}
$("#searchButton").on("click", function(e) {
e.preventDefault();
searchFunction();
})
$("#searchArea").on("change", function(e) {
e.preventDefault();
searchFunction();
});
The problem is that click and change overlap in functionality and so I get the following:
When I type something in the text box and then click elsewhere in the screen, searchFunction is fired properly but if "elsewhere" becomes the search button, then searchFunction double fires.
My question is this: Once inside the click handler, is there any way to cancel the change handler from firing as well which would otherwise cause searchFunction to double fire?
Mind that searchArea and searchButton don't have a parent-child relationship which means conventional methods like preventDefault and stopPropagation won't work.
You can see it in action in this fiddle.
Related
$(":input").on("change", function(e) {
console.log("change triggered");
$("#section").html("<button id='order'>Order</button>");
registerButtons();
});
function registerButtons() {
$("#order").on("click", function(e) {
console.log("click triggered");
alert("Hello World");
});
$("#order").on("mousedown mouseup", function(e) {
console.log(e.type + " triggered");
});
}
registerButtons();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" value="123"/>
<div id="section">
<button id="order">Order</button>
</div>
I have a web page with a button and some input fields.
On the button an click event is registered
On the input fields an change event is registered
The onChange will trigger an AJAX server call, and the result will replace parts of the web page - including the button. After AJAX result is processed, all listener are registered again.
Now the problem. A user changes the value of an input field, and clicks directly the button - but to slow (lets assume the user needs 500ms for the click), so the onChange event is fired and the page is "updated/replaced". Now the "old" button fires an onMouseDown and the "new" button fires an onMouseUp event - but no onClick.
My current workaround is, to register the two mouseDown/mouseUp events, get the timestamp of the mouse down, and if the mouse up comes in 2 seconds, do what should be done by the onClick.
It is no option to remove the button part from the AJAX response - in worst case the button could be removed and replaced by an user info.
My hope is, that there is a better solution... any ideas?
You can take advantage of the event delegation and set your listener on the container instead of the button.
You are adding a click listener to your old button and your adding a new button to the dom. So the click won't work.
The button wasn't working because for some reason it can't focus when you hover over it. So I added a getFocus method and now it should work.
$("input").on("change", function(e) {
console.log("change triggered");
$("#section").html("<button id='order'>Order</button>");
});
function registerButtons() {
$('#section').on("mouseup", '#order', function(e) {
alert('Clicked!');
});
}
registerButtons();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" value="123"/>
<div id="section">
<button id="order">Order</button>
</div>
I just found out that jQuery provides a sweet API that can be used for event delegation. This way we don't have to manually check for event target. Check it out http://api.jquery.com/on/
$("input").on("change", function(e) {
console.log("change triggered");
$("#section").html("<button id='order'>Order</button>");
});
function registerButtons() {
$("#section").on("click", '#order', function(e) {
console.log("click triggered");
alert("Hello World");
});
$("#section").on('mouseover','#order', function(e){
$(this).focus();
});
}
registerButtons();
What I want to achieve is to track form submits.
But because of the many variations that we use for the submit button I want to change my current code:
$(document).on('click','input[type="submit"], button[type="submit"]',function(){
to something that is universal. And I believe the best approach is the $("form")-annotation.
The problem is that for example if a form has an ajax script on it, it gets blocked by my additional script code.
Basically what I want to achieve is to have both worlds.
So the first one is what the website currently has (not every websites though):
$("form").submit(function () {
// ....do something, maybe ajax submit of the form.....
});
and my additional that I want to add without editing any current scripts already found in the website:
$("form").submit(function () {
$.getJSON("....");
});
The solution for me should be that the second script (the additional) will not interfere with any other form scripts.
AN IDEA
To add a class by using jQuery addClass to the forms of current page.
What is a solution for this?
I created a little Snippet to demonstrate the issue:
$(document).ready(function() {
// Registering form-submission as the first would be a possibility
$('form').on('submit', function(e) {
console.log(e.target);
console.info('My first callback is executing');
// Do some stuff here, but don't mess with the event-object
// (like preventing default or stopping the event-chain)
});
// Then afterwards everything else that *might* catch the event
$('form').on('submit', function(e) {
console.log(e.target);
console.info('My second callback is executing');
// Usually some Ajax-Handler-Callback, that handles sending the form,
// will preventDefault() and stopImmediatePropagation() - that is why
// your general first listener must be registered before any of these callbacks
console.warn('Second callback stops the event from bubbling/propagating');
e.stopImmediatePropagation();
e.preventDefault();
});
// This will never happen
$('form').on('submit', function(e) {
console.log(e.target);
console.info('My third callback will never execute');
});
// Using a delegated event-listener with `useCapture` lets this execute first
document.addEventListener('submit', function(e) {
console.info('Capturing the event natively');
}, true);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>My Website with a multi-handled form</h1>
<form class="" action="" method="post">
<input type="text" name="test" value="">
<button type="submit" name="button">Send</button>
</form>
Output of the Snippet, when submitting the form:
Capturing the event natively
<form class action method="post">…</form>
My first callback is executing
<form class action method="post">…</form>
My second callback is executing
Second callback stops the event from bubbling/propagating
What did just happened?
By pressing the submit-button, our form emits the submit-event. The Browser starts with the event-propagation in a specified event-order. There are two phases of event-propagation: event-capturing and event-bubbling.
Now our first called event-listener is the one with the useCapture-directive.
This is during the capture-phase of the event-propagation.
Explanation for useCapture taken from MDN:
capture: A Boolean that indicates that events of this type will be
dispatched to the registered listener before being dispatched to any
EventTarget beneath it in the DOM tree.
When done, the Browser starts with the bubbling-phase of the event-propagation.
This is where all $('element').on() and element.addEventListener() (without the useCapture option) registered listeners are called in their appearing order.
During this phase our second listener is not only preventing default (not submitting the form the standard-way), but also stopping the event-propagation by calling e.stopImmediatePropagation().
After that the event-chain/event-propagation stops immediately.
That is why our third listener will never execute.
On a side note: When using jQuery and exiting an event-callback with
return false, jQuery will execute e.preventDefault() and
e.stopPropagation() automatically.
See: http://api.jquery.com/on/
Conclusion
You basically have two possibilities for your scenario:
Register your default general event-listener before anything else (first event-registration in Snippet).
Register an event-listener during the capture-phase, to capture the event and handle things before the other listeners from the bubbling-phase get called (last event-registration in Snippet).
With both methods you should be able to do your stuff without interfering with other event-listeners.
Use this:
$(document).on("submit", "form", function (e){
Complete example:
<form id="form1"><input type="text" /><input type="submit" /></form>
<form id="form2"><input type="text" /><input type="submit" /></form>
Js:
$(document).on("submit", "form", function (e) {
var oForm = $(this);
var formId = oForm.attr("id");
var firstValue = oForm.find("input").first().val();
alert("Form '" + formId + " is being submitted, value of first input is: " + firstValue);
return false;
})
[JS fiddle]: http://jsfiddle.net/pZ3Jn/
What I want to achieve is to track form submits.
Why not just use $(document).on('submit', 'form', function(){});?
It will be triggered on every form submit, no matter how it is being submitted.
$(document).ready(function() {
// Some already existing event handler
$('#bir').on('submit', function(e) {
console.log($(this).attr('id'));
e.preventDefault();
});
// Your universal form event handler
$(document).on('submit', 'form', function(e) {
console.log('Additional functionality for: ' + $(this).attr('id'));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="bir">
<input type="submit" />
</form>
<form id="ikki">
<input type="submit" />
</form>
I've ran into this issue a few times before and my solution was to capture all form nodes and associate them with a special action . This may not be practical but is a possible solution for you also .
Example
//Getting all form elements
var formNodes = document.getElementsByTagName('form');
//loop through nodelist and add submit event and special class to each.
for(var i = 0; i < formNodes.length; i++){
formNodes[i].addEventListener('submit' , registerAction)
formNodes[i].className += "form-" + i;
}
/*This function captures the submitted form and determines
the action to carry out based off class name .
e.preventDefault will stop page from reloading in case of
making ajax requests.
*/
function registerAction(e){
e.preventDefault();
var formTarget = $(e.target).attr('class');
switch(formTarget){
case "form-0" :
// Do something ...
break;
case "form-1" :
// Do something else...
break;
default:
break;
}
return false;
}
Keep in mind that the logic inside registerAction can be alter to fit your needs
in this situation I used "case statement" because I feel it makes the most sense .
This is not perfect but I hope it gives you an idea..
The problem is that for example if a form has an ajax script on it, it
gets blocked by my additional script code.
No, it doesn't. You can bind many handlers on one element.
For rare cases, see the other suggestions, but If I got you right, your basic assumption was that binding a handler on an element cancel the previous one. Well, it doesn't.
I want to replace a button with an input field, where the user enters something and presses the enter button. After that, the button from the beginning should appear again. My script works so far but I can't repeat this once it finished.
Update: The button should also appear again, if the input field is shown but the user don't want to enter anything and clicks somewhere else.
The code:
<button id="createButton">Create item</button>
/*
jquery stuff
*/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#createButton').click(function( event ) {
$(this).replaceWith('<input type="text" id="buttonInput" placeholder="e.g. books, movies" autofocus>');
});
$(this).on('keypress', function (event) {
if(event.which == '13'){ // If enter button is pressed
alert('You entered something');
$('#buttonInput').replaceWith('<button id="createButton">Create item</button>');
}
});
});
</script>
Update 2: I updated the code with hide() and show() to get the same result. But how can I let the input disappear, if the user clicks somewhere inside the body, without redundancy?
The new code:
<button id="createButton">Create item</button>
<input type="text" id="input" placeholder="e.g. books, movies" autofocus>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#input').hide();
$(document).on('click', '#createButton', function (event) {
$(this).hide();
$('#input').show().focus();
});
$('#input').on('keypress', function (event) {
if (event.which == '13') { // if enter button is pressed
$(this).hide().val('');
$('#createButton').show();
}
});
});
</script>
As the other answers say, you're replacing the element (createButton), which means the click handler is no longer bound.
You can either re-bind, or bind to the parent element, with the #createButton selector using on.
$(document).on('click','#createButton', function( event ) {
...
});
Don't actually use document - use whatever the parent element is, which doesn't get replace (a div, perhaps?)
Replacing DOM elements is a bad approach though -- you'd be better off leaving the elements on the page, and using show and hide.
http://jsfiddle.net/v03j8bns/
Updated Answer
Here's a fiddle showing the show/hide/ approach. To handle:
The button should also appear again, if the input field is shown but the user don't want to enter anything and clicks somewhere else.
When the button is clicked, I call focus() on the textbox. I've also hooked up a blur() event handler, so if the user clicks/tabs out, then it'll hide the textbox and show the button.
You have to bind the click event to newly created button again:
$(document).ready(function () {
$('#createButton').click(function (event) {
$(this).replaceWith('<input type="text" id="buttonInput" placeholder="e.g. books, movies" autofocus>');
});
$(this).on('keypress', function (event) {
if (event.which == '13') { // If enter button pressed
//Disable textbox to prevent multiple submit
alert('You entered something');
$('#buttonInput').replaceWith('<button id="createButton">Create item</button>');
}
$('#createButton').bind('click', function (event) {
$(this).replaceWith('<input type="text" id="buttonInput" placeholder="e.g. books, movies" autofocus>');
});
});
});
You have this issue because you replace DOM elements. It means that your new element button no longer has click handler.
I would recommend you to use something like show/hide or use jQuery delegate on/bind for handling click.
When you're changing DOM on the fly and want to automatically assign listeners to elements that may or may not exist at certain points of time, you need to use delegated event listeners.
$(document).on('click', '#createButton', function () { ... });
$(document).on('click', '#buttonInput', function () { ... });
These handlers will work however you scramble the DOM.
I have a problem which I don't know is related to the meteor implementation of events or to Javascript events in general.
I have a textbox attached to a "change" event.
Next to it, I have a button attached to a "click" event.
When I do a change in the textbox and click the button, the click event does not fire (only the change event does). So I have to click the button two times for the click event to fire.
In Firefox, it works if I attach a mousedown event instead of the click event to the button. In Chrome it doesn't work either ways.
Minimal code reproducing the problem:
JAVASCRIPT: testevent.js
if (Meteor.isClient) {
Session.set("something", "something");
Template.hello.foo = function() {
return Session.get("foo");
};
Template.hello.something = function() {
return Session.get("something");
}
Template.hello.events({
'click .buttonid' : function () {
console.log("click !");
},
'change .textid' : function (e,t) {
console.log("change !");
var bar = e.target.value;
Session.set("foo",bar);
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
HTML: testevent.html
<head>
<title>testevent</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<input type="text" class="textid" value="{{foo}}"/>
<input type="button" class="buttonid" value="{{something}}" />
</template>
When I replace class with id the click event fire, but when I have multiple fields with the same id the events work only on one field.
The issue has to do with the hello.foo:
Template.hello.foo = function() {
return Session.get("foo");
};
and the fact that the value of foo is used to reactively populate the text input. If you remove the hello.foo function everything works as expected. When the user clicks the button, the change event fires which sets the "foo" session variable which in turn causes the template to re-render. I think the rendering process clears the remaining event queue, so the click handler never fires.
There are a couple of ways you can fix this. An easy (but crude) way is just to delay setting the session variable in the change event handler. For example:
Meteor.setTimeout(function(){Session.set("foo", bar);}, 100);
Obviously you would need to choose an appropriate delay and that may be browser/data dependent. Alternatively, you can just put the text input in its own template. For example:
<template name="hello">
{{> helloText}}
<input type="button" class="buttonid" value="{{something}}" />
</template>
<template name="helloText">
<input type="text" class="textid" value="{{foo}}"/>
</template>
After binding the events properly to this new template, you will find that helloText will be rendered separately from hello and thus your events will be preserved.
**Update: I have pasted working code in order to erase any ambiguity about what is going on. I have also tried to remove the preventDefault on both handlers, does not help*
I have a form where upon the button click, a JS event needs to happen, and the form needs to submit.
As per the code below, what I thought would happen is: alert(button), then alert(form), or vice versa. I do not care about sequence.
If i run it however, the alert(button) will show up, but the alert(form) will not.
If i comment out the code for the button, the form alert comes up.
Do i have some fundamental misunderstanding of how this is supposed to work?
jQuery(document).ready(function(){
$("form.example").submit(function(event){
event.preventDefault();
alert("form submitted");
});
$("form.example button").click(function(event){
event.preventDefault();
alert("button clicked");
});
)};
<form class="example" action="/v4test">
<button type="submit">Meow!</button>
</form>
After edit of OP
You do not need to preventDefault of the click.... only the submit... here is you working code:
jsFiddle example
jQuery(document).ready(function(){
$('form.example').submit(function(event){
event.preventDefault();
alert("form submitted");
// stop submission so we don't leave this page
});
$('form.example button').click(function() {
alert("button clicked");
});
});
old answer
You can simply put your .click() and .submit() handlers in series, and they should not cancel out. You have some syntax errors in your pseudo code.... maybe those are causing problems?
Another potential problem is that $("form button") targets the HTML <button> tags. If you use <input type="button" /> you should use $("form:button") and note that <input type="submit" /> is not a button. Anyway, I'll assume you are in fact using the <button> tags.
Usually return false is used inside .submit(function() { ... });. This stops the form from being submited through HTML. s**[topPropagation][6]** is very different. It deals with stopping events "bubbling up" to the parents of elements....... But I don't see how this would effect your case.
If you are doing a true HTML submission, make sure to put your .click() handler first, since a true HTML submission will cause you to leave the page.
If you use return false inside .submit(), the form will not be submitted through the HTML, and you'll have to handle the submission with jQuery / Javascript / AJAX.
Anyway, here is a demonstration of both the .click() and .submit() events firing in series... the code is below:
jsFiddle Example
$(function() {
$('form button').click(function() {
// Do click button stuff here.
});
$('form').submit(function(){
// Do for submission stuff here
// ...
// stop submission so we don't leave this page
// Leave this line out, if you do want to leave
// the page and submit the form, but then the results of your
// click event will probably be hard for the user to see.
return false;
});
});
The above will trigger both handlers with the following HTML:
<button type="submit">Submit</button>
As a note, I suppose you were using pseudo code, but even then, it's much easier to read, and one is sure you're not writing syntax errors, if you use:
$('form').submit(function() { /*submits form*/ });
$('form button').click(function() { /*does some action*/ });
If you put a return false on the click, it should cancel the default behavior. If you want to execute one then the other, call $('form').submit() within the click function. e.g.
$('form').submit { //submits form}
$('form button').click {
// does some action
$('form').submit();
}
There seems to be a bit of confusion about propagation here. Event propagation (which can be disabled by stopPropagation) means that events "bubble up" to parent elements; in this case, the click event would register on the form, because it is a parent of the submit button. But of course the submit handler on the form will not catch the click event.
What you are interested in is the default action, which in the case of clicking a submit button is to submit the form. The default action can be prevented by either calling preventDefault or returning false. You are probably doing the latter.
Note that in Javascript functions which do not end with an explicit return do still return a value, which is the result of the last command in the function. You should end your click handler with return; or return true;. I have no idea where I got that from. Javascript functions actually return undefined when there is no explicit return statement.
Does clicking the button submit the form? If so:
// Disable the submit action
$("form").submit(function(){
return false;
});
$("form button").click(function(){
// Do some action here
$("form").unbind("submit").submit();
});
If you don't unbind the submit event when you click the button, the submit will just do nothing.