I was wondering if Javascript or jQuery have a way to delete an event listener. Let's say I want to make a function that I want to trigger only once, for example let's say I want to have a button that shows some hidden elements on the document, I would make this function (assuming the hidden elements have a hidden class that hides them):
jQuery('#toggler').click(function() {
console.log('Hidden elements are now shown');
jQuery('.hidden').removeClass('hidden');
});
Simple enough, right ? Now, my actual problem comes in, I don't want jquery to run that function again and again each time the button is clicked, because the elements are already revealed, so is there a clean way to do it ? So, in this example after clicking the toggler multiple times I want to get only one console message.
I could do jQuery(this).unbind('click'), but this results into removing ALL triggers and I only want to remove the current trigger.
What I usually do when I face such scenarios is solve it like this (which is ugly and doesn't actually prevent code execution, but only handles the code's results) :
var toggler_clicked = false;
jQuery('#toggler').click(function() {
if(toggler_clicked) return;
toggler_clicked = true;
console.log('Hidden elements are now shown');
jQuery('.hidden').removeClass('hidden');
});
Also I don't want to use jQuery's one, because I will have the same problem when I'll need to delete the trigger conditionally, so if you can help please give me a dynamic answer.
Thanks in advance !
You have to name your function like that:
var myFunction = function() {
console.log('Hidden elements are now shown');
jQuery('.hidden').removeClass('hidden');
};
And bind it this way
jQuery('#toggler').click(myFunction);
Then you can unbind it with :
jQuery('#toggler').off('click',myFunction);
Without unbinding the other listeners
You can try this:
var myFunc = function() {
console.log('Hidden elements are now shown');
jQuery('.hidden').removeClass('hidden');
jQuery(this).unbind('click', myFunc);
};
jQuery('#toggler').click(myFunc);
This way of calling unbind is such that only the listener for myFunc handler is removed and not all the events connected to the click on the toggler.
I would use the .on() and its opposite .off() methods to attach/detach the event handler. It is the recommended way since 1.7 instead of the .bind() and .unbind() versions that became deprecated as of jQuery 3.0.
$("#toggler").on("click", function(event) {
console.log('Hidden elements are now shown');
$('.hidden').removeClass('hidden');
// if (/* Add your condition here */) {
$(this).off(event);
// }
});
$("#toggler").on("click", function(event) {
console.log('Hidden elements are now shown');
$('.hidden').removeClass('hidden');
// if (/* Add your condition here */) {
$(this).off(event);
// }
});
.hidden {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="toggler">Toggle</button>
<div class="hidden">
HIDDEN
</div>
Try this
var myFunction = function() {
console.log('Hidden elements are now shown');
jQuery('.hidden').removeClass('hidden');
};
Add the event listener like this:
jQuery('#toggler').addEventListener("click", myFunction);
And remove it like this:
jQuery('#toggler').removeEventListener("click", myFunction);
So all together this will do the trick:
var myFunction = function() {
console.log('Hidden elements are now shown');
jQuery('.hidden').removeClass('hidden');
jQuery('#toggler').removeEventListener("click", myFunction);
};
jQuery('#toggler').addEventListener("click", myFunction);
more about the HTML DOM removeEventListener() Method
Jquery unbind function takes 2 parameters eventType and handler
You can put your event listener into separate function like this:
var clickEventHandler = function(){
//your logic goes here
}
After you add listener as reference:
jQuery('#toggler').click(clickEventHandler);
And then, later, anytime, anywhere you want you can unbind that specific handler:
jQuery('#toggler').unbind('click', clickEventHandler);
What i used to do in the past is toggle the click behavior using css classes, ex i used to set a click listener on the parent and delegate to all of the children something that jquery is doing now by default i believe. Anyway based on the css class it will trigger an event for ex.
$('.some-parent-element').on(
'click',
'the-behavior-css-class',
function() { // do stuff here.... }
)
Now if you want to remove this behavior you can just toggle the class of the element and it should do the job. ex
$('.some-parent-element').on(
'click',
'hide-me-on-click-or-whatever',
function() {
$(this).toggleClass('hide-me-on-click-or-whatever')
// perform the action
}
)
You can check if the element has the class hidden
Related
I just started to learn js and need a little help: I have the following function:
//SET CHAT BEHAVIOR
function chatSettings() {
console.log('ChatSettings called')
function BtnAndScrollBar(texteditor) {
console.log('BTNAndScrollBar called');
const sendBtn = $('.cl.active').find('.sendBtn');
const attachBtn = $('.cl.active').find('.attachBtn');
console.log(sendBtn)
}
function sendAndDeleteMessage(send) {
console.log(send);
}
var sendBtn = $('.cl.active').find('.sendBtn');
sendBtn.mousedown(function () {
sendAndDeleteMessage(this);
});
var textEditor1 = $('.cl.active').find('.chatTextarea');
textEditor1.on('focus change mousedown mouseout keyup mouseup', function (){
console.log(this);
BtnAndScrollBar(this)
});
}
$('document').ready(function () {
console.log('hello');
$('.tabs').tabs();
chatSettings();
});
I prepared a js.fiddle - As you can see from console.log when clicking into the textarea, the eventListener always listens to #cl1, even if .cl.active switches along with the according TAB.
The events in the textarea are just relevant, if .cl is active. My target is to wrap all three eventListener into one and apply the event to the textarea in the active stream, but all I tried went wrong... Can anyone help? #Dontrepeatyourself #DRY
$(".chatTextarea").on(
'focus change mousedown mouseout keyup mouseup',
function (this) {
//this.id can contain the unique id
greatFunction(this);
});
This will bind event individually with unique id found with this keyword and also wraps all event listener into one function but this is better when you want to process each event with same functionality
please let me know if this helps.
Peace
$(".cl textarea").on('focus change mousedown mouseout keyup mouseup', function () {
greatFunction(this)
});
Tada!
P.S. Is there a reason greatFunction is defined inside window.onload?
Try using $(document).ready function to load code when the page loads.
Also use $('textarea #cl1').on to get the textarea with the #cl1 or whichever id you want to use and then call the function after using the .on.
Hope this helps!
Let me know if it works!
$(document).ready(function () {
function greatFunction(elem) {
//do stuff
}
$('textarea').on('focus change mousedown mouseout keyup mouseup', function () {
greatFunction(this)
});
}
First off, I changed the onload to bind with jQuery, so all your logic is doing jQuery bindings, rather than swapping back and forth between jQuery and vanilla javascript. Also, doing an actual binding removes an inline binding.
Next, the binding has been condensed into a single delegate event listener. Since you eluded in your comments that it wasn't working for the active element after the active was moved or added, this reflected that you were dealing with dynamic elements. Delegate event listeners are one way to handle such things.
Delegate event listeners bind on a parent element of the elements that will change, or be created. It then waits for an event to happen on one of it's children. When it gets an event it is listening for, it then checks to see if the element that it originated from matches the child selector (second argument) for the listener. If it does match, it will then process the event for the child element.
Lastly, I added some buttons to swap around the active class, so you could see in the snippet that the event handler will start working for any element that you make active, regardless of it starting out that way.
$(window).on('load', function () {
function greatFunction (elem) {
console.log(elem.value);
}
$(document.body).on(
'focus change mousedown mouseout keyup mouseup',
'.cl.active .chatTextarea',
function () {
greatFunction(this);
}
);
$('.makeActive').on('click', function () {
$('.active').removeClass('active');
$(this).closest('div').addClass('active');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="cl1" class="cl active"><textarea class="chatTextarea">aa</textarea><button class="makeActive">Make Active</button></div>
<div id="cl2" class="cl"><textarea class="chatTextarea">bb</textarea><button class="makeActive">Make Active</button></div>
<div id="cl3" class="cl"><textarea class="chatTextarea">cc</textarea><button class="makeActive">Make Active</button></div>
i need to trigger only one click on specific element that can be on page load time or added dynamically in the future. Some code
This code work just fine for elements that are rendered on load time but wont bind the click event to new elements dynamically added
$(".message-actions .accept").one("click", function(e){
console.log("accept");
});
In the other hand if i do it this way, it will bind the event to new elements but don't unbind the event so if i click it again it will print the same console log
$("body").on("click", ".message-actions .accept", function(e){
console.log("decline");
$(this).unbind("click");
});
At last if i do it in this other way it will only fire the event in the first element i click even if there is more than one loaded or added after.
$("body").one("click", ".message-actions .accept", function(e){
console.log("decline");
});
How can i do this?
Thanks
You can add data to the element that remembers whether the handler has run before:
$("body").on("click", ".message-actions .accept", function() {
if (!$(this).data("run-once")) {
console.log("decline");
$(this).data("run-once", true); // Remember that we ran already on this element
}
});
I would do it this way:
var handleClick = function () {
// do your work
alert("decline");
// unbind
$("body").off("click", ".message-actions .accept", handleClick);
};
$("body").on("click", ".message-actions .accept", handleClick);
Check this fiddle
You can solve it like this, if it suits your situation : http://jsfiddle.net/hu4fp5qs/1/
$("body").on("click",".message-actions .accept",function(){
$(this).removeClass("accept").addClass("decline");
alert("Declined");
});
On click remove class accept and add class decline.
This will help you in styling both the cases differently so that you can distinguish between them.
.accept{
background-color:green;
}
.decline{
background-color:red;
}
I am really tired of this pattern:
$("#moveButton").click(function() {
if ($(this).hasClass("disabled")) {
return;
}
//do something
});
I want to intercept all click events for elements, that have class "disabled" dynamically added.
I tried this:
$('input,a,div').click(function(event){
if ($(this).hasClass("disabled")) {
event.preventDefault()
event.stopPropagation();
}
});
but somehow it is not working.
This script is on the top of my page.js
UPDATE
The pitfall is that 'disabled' class can be added dynamically.
So ones you have already added event listener to a button, there have to be a solution to INTERCEPT all its click handlers and check if this element is disabled now. Than if it is, stop this event to be catched by handlers.
Just filter out those elements then ?
$('input,a,div').not('.disabled').on('click', function(){
// do stuff
});
if the class is added later, you can use a delegated event handler, and if you really want to return false for all elements that has that class or are within such an element:
$('input,a,div').on('click', function(e) {
if ( $(e.target).closest('.disabled').length ) return false;
});
EDIT:
as noted above, you can filter out classes added later in the event handler, if the handler is delegated, like this example:
//bind an event handler to all DIV element that does NOT have a .disabled class
$(document).on('click', 'div:not(.disabled)', function() {
alert('ok');
});
// even if we add the class later, the event handler above will filter it out
$('.test').eq(1).addClass('disabled');
EDIT
use this code
$('input,a,div').click(function(event){
if ($(this).hasClass("disabled")) {
return false;
}
});
SOLVED:
I have found two ways of interception:
1) Not an interception. Exactly as #adeneo said, we can use .on( with test method when attaching event handlers:
$(document).on('click', 'div:not(.disabled)', function() {
alert('ok');
});
see his answer for more details and upvote his answer if you find this helpful.
2) We can put this code ON TOP for executing first, but make sure you add it when DOM is rendered:
$("a,div,input").click(function(event){
if($(this).hasClass('disabled')){
event.stopImmediatePropagation()
}
});
And this will prevent all existing .click, .on('click', and .live('click' handlers from being executed. If they have no other parameters.
This solution is good if you already have tons of handlers and don't want to rewrite it.
EXAMPLE
You can use the pseudo class :disabled:
$('input:disabled,a:disabled,div:disabled').click(function() {
return false;
});
Ok so I'm trying to start/stop a setInterval function by changing the id of an element and applying two different functions to the different id's. here's the code at the moment:
$(document).ready(function(){
var seq
$('#start').click(function(){
$(this).attr('id','stop');
seq=self.setInterval(function(){blah()},125);
});
$('#stop').click(function(){
$(this).attr('id','start');
clearInterval(seq);
});
});
when I click the #start element, the setInterval starts and the id changes to #stop, but if I click again (on the element now called #stop), the code for #start is carried out (another setInterval is added)
thanks
the function 'blah' is just an example of a made up function
When you say:
$('some selector').click(...
That binds a click handler to all elements that match some selector at that moment - it doesn't automatically apply to elements that might match it in future.
To have the handler apply to elements that match the selector at the time of the click event you need to use a delegated event handler, which means attaching the handler to a parent element (or to document if the element has no static parent):
$(document).ready(function(){
var seq
$(document).on('click', '#start', function(){
$(this).attr('id','stop');
seq=self.setInterval(function(){blah()},125);
});
$(document).on('click', '#stop', function(){
$(this).attr('id','start');
clearInterval(seq);
});
});
The .on() method lets you attach "normal" non-delegated handlers or delegated handlers depending on the parameter you pass to it.
The other option is to not change the id and just use a single click handler that tests the current state in some manner:
$(document).ready(function(){
var seq;
$('#start').click(function(){
if (seq) {
clearInterval(seq);
seq = null;
} else
seq=self.setInterval(function(){blah()},125);
});
});
I would NOT change the id since this will not change the event bindings. You generally should never change an elements id tag. The better plan (at least to me) is to set it as a class. Then use html data tags to decide what the state is.
<button type="button" class="buttonClass" data-state="start">Button Text</button>
$(document).ready(function(){
var seq
$('.buttonClass').click(function(){
var state = $(this).data('state');
if(state=='start') {
//do start interval stuff
seq=self.setInterval(function(){blah()},125);
$(this).data('state','stop'); //Change the state now
} else if(state=='stop') {
//do stop interval stuff
clearInterval(seq)
$(this).data('state','start'); //Change the state again
}
});
});
Why don't you just check for the state like this (no need to add event listener to document):
$(document).ready(function(){
var seq;
$('#start').on('click', function(){
if(!$(this).hasClass('running')){
seq=self.setInterval(function(){blah()},125);
$(this).addClass('running');
}
else{
$(this).removeClass('running');
clearInterval(seq);
}
});
});
lets say I have
function trigger(){
$('a.pep').each(function(){
$('a.pep').click(function(){
console.log($(this).val());
});
});
}
function push(){
$('body').append('<a class="pep">hey mate i have no trigger yet</a>');
trigger(); //now i do but the others have duplicated trigger
}
$(document).ready(function(){
$('a.push').click(function(){
push();
});
});
So it seems that the click event is being applied twice/+ because the console.log is lauched more than once by click
How can i prevent this?
The problem is that you call $('a.pep').click() lots of times. (In fact, you bind as many click handlers as there are matching elements to each element. And then you do it again every time one of them is clicked.)
You should lever the DOM event bubbling model to handle this. jQuery helps you with the on method:
$(document.body).on('click', 'a.pep', function() {
console.log('element clicked');
$(document.body).append('<a class="pep">Click handlers handled automatically</a>');
});
See a working jsFiddle.
Note that I have removed the val call, because a elements can't have a value... Note also that the on method is introduced in jQuery 1.7; before that, use delegate:
$(document.body).delegate('a.pep', 'click', function() {
Small change to your trigger function is all you need. Just unbind the click event before binding to ensure that it is never added more than once. Also, you don't need to use each when binding events, it will add the event to each item automatically.
function trigger(){
$('a.pep').unbind('click').click(function() {
console.log($(this).val());
});
}
You can check using data('events') on any element if the required event is attached or not. For example to check if click event is attached or not try this.
if(!$('a.pep').data('events').click){
$('a.pep').click(function(){
console.log($(this).val());
});
}
you should use jQuery live here because you add DOM elements dynamicly and you want them to have the same click behaviour
function push(){
$('body').append('<a class="pep">hey mate i have no trigger yet</a>');
}
$(document).ready(function(){
$('a.push').click(function(){
push();
});
$('a.pep').live('click', function(){
console.log($(this).val());
});
});
Try:
if($('a.pep').data('events').click) {
//do something
}
i think if you use live() event you dont need to make function
$('a.pep').live('click', function(){
console.log($(this).val());
});