Separate click event from focus event in Angular.js - javascript

I want to add a click handler to an ng-focus callback, to start listening to all future click events. But when focus is initiated by a click, the click handler fires immediately as well. I can solve the problem with a $timeout, but this is a hack and it goes against everything I stand for. What have I become?!
My HTML:
<input type="text" name="q" ng-focus="inputFocus($event)" />
This .js works (this is inside a directive's link function):
scope.inputFocus = function (e) {
scope.displayHistory = true;
$timeout(function () {
$document.on('click', hideHistory);
}, 200)
};
function hideHistory() {
scope.displayHistory = false;
scope.$digest();
$document.off('click')
};
I want the following to work, but the problem is that the click callback fires immediately. As you might expect, if I tab to this field, the click callback does not get called. But if the focus event is triggered by a click, hideHistory gets called.
scope.inputFocus = function (e) {
scope.displayHistory = true;
$document.on('click', hideHistory);
};
I tried calling e.stopPropagation() and e.stopImmediatePropagation() but these solutions were not effective either.

Have you tried e.preventDefault(); ?
Anyway you can provide a fiddle to illustrate exactly what's going on?

Related

JavaScript event handling code get's called multiple times

I need your help. I'm currently working with a modal lib in JavaScript to display a modal for my customers:
https://github.com/vodkabears/Remodal/tree/1.1.1
Unfortunately my event handling in case the user clicks a button don't works like expected. When you take a look into the manual, you can see under the point Events the following event handler:
$(document).on('cancellation', '.remodal', function () {
console.log('Cancel button is clicked');
});
This one get's triggered for example when the cancel button get's pressed. Because I'm using one popup for multiple things, I need to attach the event handler to the call directly. So first I've wrote a function that opens the popup:
function openRemodal( remodalId ) {
let remodal = $( `[data-remodal-id=${remodalId}]` );
remodal.remodal().open();
return remodal; // <- added to handle events
}
I can call this function that way:
openRemodal( 'information-remodal' );
To get an event handling done, I've now returned the remodal in the function and re-wrote my call:
openRemodal( 'information-remodal' ).on( 'cancellation', function () {
alert( 'Test' );
} );
This seems to work but somehow when I repeat the opening of the popup and pressing the button, the alert get's shown multiple times increased by any new opening.
I'm not sure why this happens and why. Can you please help me get this working? I just want to call any function in there once - any time.
JQuery has a .one method ... try using that in place of .on. The callback should run only once. https://api.jquery.com/one/
On each time when you open model you attach function to cancelation event. so add new function and you never remove it. after first time you have one, then you have two... etc.
just attach it once, or remove it after handling event.
const modal = openRemodal( 'information-remodal' )
const handler = () => {
alert( 'Test' );
modal.off('cancellation', handler);
}
modal.on( 'cancellation', handler);

eventListener firing multiple times and increasing

On a click function I have the option of playing audio.
The click is only fired once (after I added .off(), which I seem to have to do for every click event because I think there's something I fundamentally don't get about how javascript works) but the function added to the "ended" listener shows it is firing the number of times the button has been clicked. I presume .play() is also being fired multiple times.
These need to be inside the click event to get the id so how do I stop these kinds of things from happening, here and elsewhere when using js? Adding event.stopPropagation(), event.bubbles = false and .off() everywhere seems unnecessary (and in this case doesn't make a difference anyway).
$('.button').off().on('click', function(event){
event.stopPropagation();
event.bubbles = false;
var id = $(this).attr('id')
if ($(this).hasClass('hasAudio')) {
document.getElementById('audio_'+id).play();
document.getElementById('audio_'+id).addEventListener("ended", function(){
console.log("ended");
});
}
});
Move the ended event outside the click event,you are registering the event each time you click on the button
$('.button').on('click', function(event){
var id = $(this).attr('id')
if ($(this).hasClass('hasAudio')) {
document.getElementById('audio_'+id).play();
}
});
$('[id^="audio_"]').on("ended", function(){
console.log("ended");
});
Each time you click on the button a new event listener will be added to the ended event. To prevent that you can try defining the callback function before hand. That will prevent your event listener to be added in the event loop over and over.
An anonymous function has no signature, hence when you define the event with it, it will think that this is supposed to be a new event listener and invokes it multiple times. Check the working snippets to see the difference. Type something in the input box to see what is happening.
If this is confusing then removeEventListener can be the next option.
function ended(event){
console.log("ended");
}
$('.button').off().on('click', function(event){
event.stopPropagation();
event.bubbles = false;
var id = $(this).attr('id')
if ($(this).hasClass('hasAudio')) {
document.getElementById('audio_'+id).play();
document.getElementById('audio_'+id).addEventListener("ended", ended);
}
});
var input = document.getElementById('some');
function callback(event) {
console.log("PRINT");
}
input.addEventListener("keyup", callback)
// input.removeEventListener("keyup", callback)
input.addEventListener("keyup", callback)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="some" value="" >
Anonymous function as callback
var input = document.getElementById('some');
input.addEventListener("keyup", function(event) {
console.log("PRINT");
})
// input.removeEventListener("keyup", callback)
input.addEventListener("keyup", function(event) {
console.log("PRINT");
})
<input id="some" value="">
This fails because, every time you click the function, you add a new event listener to the button.
document.getElementById('audio_'+id).addEventListener("ended", function(){
console.log("ended");
This is repeatedly adding the event listener to the button.If you need this inside the click event, check to see whether it exists already. If it does, don't add it again.
Use global flag which defines if you want to pause or play. and also use preventDefault (in case of any inline click event used).
You have to remove the registered event listener after your task is completed.
document.getElementById('audio_'+id).removeEventListener("ended", function(){
console.log("ended");
});
Or what you can do is that move the logic for registering event listener outside the click event listener. Like this the event will be registered only once.
document.getElementById('audio_'+id).addEventListener("ended", function(){
console.log("ended");
});
}
$('.button').off().on('click', function(event){
event.stopPropagation();
event.bubbles = false;
var id = $(this).attr('id')
if ($(this).hasClass('hasAudio')) {
document.getElementById('audio_'+id).play();
});

Dont use beforeunload on clicking a specific element (jquery, js)

Can someone explain me, how to improve my code, so that it will:
Not call beforeunload event if I'll click on button with class
.btn?
And, if it's possible, without tracking each click event.
I mean:
$(document).on("click", ....);
Here's my current code. It's working, but it prevents page reloading on each action (close, back, click on any button etc)
var reload = $(document).find("[data-prevent-reload]");
if(reload.data('prevent-reload')) {
window.addEventListener('beforeunload', function (ev) {
return ev.returnValue = 'STOP!';
});
}
There is no event that tells you what triggered it so you are stuck with listening to the click events. It is normally done with boolean and check it before calling it.
var ignoreUnload
$(document).on("click", ".btn", function () {
ignoreUnload = true
});
window.addEventListener('beforeunload', function (ev) {
if(!ignoreUnload) return ev.returnValue = 'STOP!';
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a class="btn" href="https://example.com">btn</a>
<a class="" href="https://example.com">not</a>
other option could be to unbind the event instead of the boolean.

Differentiate change event incurred by user or script

I've encountered a situation where some script changes a select/radio/checkbox. That fires the change event. However I need, separately, an event to tell me the user changed the element, and obviously when change fires from the script it registers as a user change.
Is there a way to prevent a script from firing the change event when you alter the value of a select/radio/checkbox?
Use jQuery.trigger() and pass an additional parameter when triggering the event from code.
For example:
$('select').bind('change', function(e, isScriptInvoked) {
if (isScriptInvoked === true) {
// some code for when artificial
} else {
// some code for when organic
}
});
// Trigger the event.
$('select').trigger('change', [true]);
Try:
function changeEvent(){
if(this.type.toLowerCase()=='select' || this.type.toLowerCase()=='radio' || this.type.toLowerCase()=='checkbox')
return false;
else{
//Your code here
}
}

Call a function after form reset

Is there a method for me to call a function after click on the reset button in form, and I mean after, so that the form is first reset and then my function called. Normal event bubbling would call my function and only then reset the form. Now I would like to avoid setTimeout in order to do this.
What I need is to call a function when a form is reset because I use uniform and uniform needs to be updated when values change.
At the moment I do it like this:
//Reset inputs in a form when reset button is hit
$("button[type='reset']").live('click', function(){
elem = this;
//Sadly we need to use setTimeout to execute this after the reset has taken place
setTimeout(function(){
$.each($(elem).parents('form').find(":input"), function(){
$.uniform.update($(this));
});
}, 50);
});
I tried to do al this on $(':input').change() but reseting an element does not seem to trigger the change event.
Thank you in advance for any help.
HTML forms do have an onReset event, you can add your call inside there:
function updateForm()
{
$.each($('form').find(":input"), function(){
$.uniform.update($(this));
});
}
<form onReset="updateForm();">
As pointed out in the comment by Frédéric Hamidi you can also use bind like so:
$('form').bind('reset', function() {
$.each($(this).find(":input"), function(){
$.uniform.update($(this));
});
});
After some testing it appears both ways fire before the reset takes place and not after. The way your doing it now appears to be the best way.
The same conclusion was found in this question here
I haven't yet tested in all browsers, but you can do your own ordering within a click event:
http://jsfiddle.net/vol7ron/9KCNL/1/
$(document).ready(function() {
$("input:reset").click(function() { // apply to reset button's click event
this.form.reset(); // reset the form
window.alert($("input:text").val()); // call your function after the reset
return false; // prevent reset button from resetting again
});
});
Time ago I worked debugging a Google IE related plugin and I solved the main error with a bubbling trick. That's why I think immediately in this solution for your problem (of course should be cross-browser):
<form>
<div id="capture_bubble">
<input type="text"><input type="reset">
</div>
</form>
In this way you can capture the bubbling with $('#capture_bubble') after reset event be triggered.
You can make a quick test with:
(function($) {
$(function() {
$('#capture_bubble').live('click', function(){
console.debug('capture_bubble');
alert('capture_bubble')
})
$("input[type='reset']").live('click', function(){
this.form.reset(); // forcing reset event
console.debug('reset');
alert('reset')
});
});
})(jQuery);
Please note: this.form.reset(); (change made due to a jeff-wilbert observation)
you shouldn't need to wait 50 milliseconds. If you use setTimeout with a timeout of zero, it effectively means "push this code onto the event stack". Since the form-reset is guaranteed to have fired first, the code in the setTimeout is guaranteed (in well behaved javascript interpreters) to have access to the form values you want (post-reset). You should be able to use the code below, guilt-free.
var afterReset = function(){
var pushMeOntoTheEventStack = window.setTimeout(function(){
$("#form input").each(function(){
console.log( this.name + ' = ' + this.value );
});
},0);
};
$("#form").on("reset",afterReset);
Try this solution
Goal:
add on "click" event
prevent the default action (reset)
trigger "reset"
run desired code
Example:
$("button[type='reset']").on('click', function(evt) {
evt.preventDefault();
$(evt.target).trigger('reset');
// enter code to run after reset
$.each($(this).find(":input"), function(){
$.uniform.update($(this));
});
});

Categories