I'm using JQuery inside EcmaScript 6 class, and I have an event function which fires on instantiation of the class and the event contains different JQuery events which need to interact with the Class , so I do .bind() to achieve that, all works ok except one event which for some reason overrides this that belongs to jquery element "this" with "that" which I passed with .bind(that) method, here is my code (everything works exept for this event) :
var that = this;
$(document).on('click', '[select-file]' , function(event) {
event.preventDefault();
console.log(this);
}.bind(that));
so the console log gives me the parent class instead of jquery element
where as this works as expected:
$(document).on('click', '[open-file-dialoge]', function(event) {
event.preventDefault();
$('[file-dialoge]').modal('show');
if ($(this).attr('mitiupload') == 'false') {
// check if multiple upload is disabled
that.multiUpload = false;
$(this).removeAttr('multiple');
}
that.insertFiles();
}.bind(that));
Pleas help , I can't understand what is going on here one does not work as expected even though there is no big difference between them ;(
Function#bind() changes the context of this in a function. If you want the current element you can use event.currentTarget
var that = {};
$(document).on('click', 'button', function(event) {
event.preventDefault();
var el = event.currentTarget;
console.log('this=', this);
console.log('el=', el)
}.bind(that));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="test">Click me</button>
Related
I am trying to develop some code to allow the user show/hide a block level element by clicking a button.
The HTML structure is like below
<div class="chat_container"><a class="crm" href="https://google.com" target="_blank">Chat?</a><button id="close_chat"><</button></div>
I have written a click() function for #close_chat which amongst other things changes the ID of the button to #open_chat. I then use the on() method on #open_chat to modify some classes and ids on various elements. In isolation both these methods work, however when combined they don't work. I have noticed that when I click #close_chat even though the ID changes to #open_chat the original event is still attached to the button. After doing some search I suspected the issue might have been related to events bubbling up, but now I am not so sure, still I added event.stopPopagation() to my click function and I can see it appears to be called correctly. I have also tried using the one() method, this appeared to get closer to the behavior I was expecting at the DOM level but still didn't working
My expected behavior is the click() function is called when the user clicks #close_chat, the event is then unbound allowing the .on() event to be called on #open_chat. Id than of course have to reset the original functionality. My code looks like this
$(document).ready(function () {
var close = "<button id='close_chat'><</div>";
var container = $("<div />");
container.addClass("chat_container");
var crmChat = $("<a />");
crmChat.addClass("crm");
crmChat.attr("href", "https://google.com");
crmChat.attr("target", "_blank");
crmChat.text("Chat?");
console.log(crmChat);
console.log(container);
$(container).insertAfter("#heading");
$(container).prepend(crmChat);
$(close).insertAfter(crmChat);
$("#close_chat").click(function (event) {
$("#close_chat").removeAttr("id").attr("id", "open_chat");
event.stopPropagation();
alert(event.isPropagationStopped());
//return false;
});
$(".chat_container").on("#open_chat", "button", function () {
//$(".crm_chat_container").addClass("animate-open").removeClass("animate-close");
$("#open_chat").html(">").removeAttr("id").attr("id", "reopen");
//event.stopPropagation();
});
});
any help is greatly appreciated
Sam
edit, I have now updated my code to look like so
//onclick function for our close button
$("#close_chat").click(function (event) {
attachClosedChatListner();
});
function attachOpendChatListener() {
$(".chat_container").on("click","#open_chat", function () {
$("#open_chat").removeAttr("id").attr("id", "close_chat");
$("#close_chat").html("<")
$(".crm_chat_container").removeClass("animate-close").addClass("animate-open");
});
//attachClosedChatListner();
}
function attachClosedChatListner() {
$("#close_chat").off('click');
$("#close_chat").removeAttr("id").attr("id", "open_chat");
$("#open_chat").html(">")
$(".chat_container").removeClass("animate-open").addClass("animate-close");
//attachOpendChatListener();
}
What about re-attaching the event?
$("#close_chat").click(function (event) {
$("#close_chat").removeAttr("id").attr("id", "open_chat");
attachOpenChatListener();
event.stopPropagation();
alert(event.isPropagationStopped());
//return false;
});
function attachOpenChatListener() {
$("#close_chat").off('click');
$(".chat_container").on("#open_chat", "button", function () {
//$(".crm_chat_container").addClass("animate-open").removeClass("animate-close");
$("#open_chat").html(">").removeAttr("id").attr("id", "reopen");
//event.stopPropagation();
});
}
I managed to work this out, the click function was causing the problem
//onclick function for our close button
$("#close_chat").click(function (event) {
attachClosedChatListner();
});
I've replaced it with .on and it works now
$(".crm_chat_container").on("click", "#close_chat", function (event) {
$("#close_chat").off('click');
$("#close_chat").removeAttr("id").attr("id", "open_chat");
$("#open_chat").html(">");
$(".crm_chat_container").removeClass("primo-animate-open").addClass("animate- close");
attachCloseChatListener();
event.stopImmediatePropagation();
});
function attachCloseChatListener() {
$(".crm_chat_container").on("click", "#open_chat", function (event) {
$("#open_chat").off('click');
$(".crm_chat_container").removeClass("primo-animate-close").addClass("primo-animate-open");
$("#open_chat").removeAttr("id").attr("id", "close_chat");
$("#close_chat").html("<");
event.stopImmediatePropagation();
});
}
on thing is my click events appears to be firing multiple times, that is after clicking my buttons a few times I see several click events in dev tools.
Anyway, thanks for putting me on the right path
I am trying to let Jq listen to three buttons at the same onclick method
then trigger a function and call the clicked button by $(this);
here is a sample :
$("body").on('click', 'a.home:visible', 'a.mobile:visible', 'a.phone:visible', function () {
var attr = $(this).attr('attr');
$(this).parents('.dropdown-menu').prev().prev().text(attr);
});
You did it basically correct. Your approach is fine. But you have to combine it in one string, not as single parameters. And you don't need :visible, because you can't click on invisible elements. ;)
$("body").on('click', 'a.home, a.mobile, a.phone', function() {
var attr = $(this).attr('attr');
$(this).parents('.dropdown-menu').prev().prev().text(attr);
});
If the elements are static you should even use a normal event listener instead of a delegation.
$('a.home, a.mobile, a.phone').click(function() {
var attr = $(this).attr('attr');
$(this).parents('.dropdown-menu').prev().prev().text(attr);
});
Put them in one quotes
$("body").on('click', 'a.home:visible,a.mobile:visible,a.phone:visible', function() {
alert('Clicked')
});
JSFIDDLE
I have a bunch of elements that get three different classes: neutral, markedV and markedX. When a user clicks one of these elements, the classes toggle once: neutral -> markedV -> markedX -> neutral. Every click will switch the class and execute a function.
$(document).ready(function(){
$(".neutral").click(function markV(event) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
$(this).unbind("click");
$(this).click(markX(event));
});
$(".markedV").click(function markX(event) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
$(this).unbind("click");
$(this).click(neutral(event));
});
$(".markedX").click(function neutral(event) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
$(this).unbind("click");
$(this).click(markV(event));
});
});
But obviously this doesn't work. I think I have three obstacles:
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
There's no need to constantly bind and unbind the event handler.
You should have one handler for all these options:
$(document).ready(function() {
var classes = ['neutral', 'markedV', 'markedX'],
methods = {
neutral: function (e) { alert('Good!') },
markedV: function (e) { alert('Bad!') },
markedX: function (e) { alert('Ok!') },
};
$( '.' + classes.join(',.') ).click(function (e) {
var $this = $(this);
$.each(classes, function (i, v) {
if ( $this.hasClass(v) ) {
methods[v].call(this, e);
$this.removeClass(v).addClass( classes[i + 1] || classes[0] );
return false;
}
});
});
});
Here's the fiddle: http://jsfiddle.net/m3CyX/
For such cases you need to attach the event to a higher parent and Delegate the event .
Remember that events are attached to the Elements and not to the classes.
Try this approach
$(document).ready(function () {
$(document).on('click', function (e) {
var $target = e.target;
if ($target.hasClass('markedV')) {
alert("Good!");
$target.addClass("markedV").removeClass("neutral");
} else if ($target.hasClass('markedV')) {
alert("Bad!");
$target.addClass("markedX").removeClass("markedV");
} else if ($target.hasClass('markedX')) {
alert("Ok!");
$target.addClass("neutral").removeClass("markedX");
}
});
});
OR as #Bergi Suggested
$(document).ready(function () {
$(document).on('click', 'markedV',function (e) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
});
$(document).on('click', 'markedX',function (e) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
});
$(document).on('click', 'neutral',function (e) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
});
});
Here document can be replaced with any static parent container..
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
You don't bind elements to functions, you bind handler functions to events on elements. You can't use a function before it is defined (yet you might use a function above the location in the code where it was declared - called "hoisting").
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
That is what happens implicitly when the handler is called. You only need to pass the function - do not call it! Yet your problem is that you cannot access the named function expressions from outside.
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
Yes. Use only one handler, and decide dynamically what to do in the current state. Do not steadily bind and unbind handlers. Or use event delegation.
I would like a cross modern browser way to take a mouse event from one html element and pass it on to another.
eg.
el1.addEventListener('mousemove', function(e) {
el2.trigger('mousemove', e);
});
el2.addEventListener('mousemove', function(e) {
//THIS SHOULD BE CALLED WHEN EITHER el1
});
a jquery solution is ok but I would prefer a non-jquery solution. Is this simple?
Here is the correct code
var el1 = document.getElementById('el1');
var el2 = document.getElementById('el2');
el1.onmousemove = function(e) {
alert('el1 event');
el2.onmousemove(e);
};
el2.onmousemove = function(e) {
alert('el2 event');
};
demo
This is good if you want the event argument e to pass over to el2's event. This updated demo shows mouse position being passed over.
native should work like that
var evt;
el1.onmousemove = function() {
evt = document.createEvent('MouseEvent');
evt.initEvent('mousemove', true, true);
el2.dispatchEvent(evt);
}
You can read up on element.dispatchEvent here:
https://developer.mozilla.org/en-US/docs/DOM/element.dispatchEvent
If you accept a jQuery answer then here's the code:
// el1 and el2 are the jQuery objects of the DOM elements
el1.mousemove(function (event) {
el2.mousemove(event);
});
In pure javascript:
el1.onmousemove = function(e) {
el2.onmousemove('mousemove', e);
};
el2.onmousemove = function(e) {
};
el1.addEventListener('mousemove', handler, false);
el2.addEventListener('mousemove', handler2, false);
function handler(e) {
// do some stuff
handler2.call(el2, e); // pass el2 as this reference, event as the argument.
};
not too sure if this is what you are looking for, just name the event handlers and trigger the one off the other.
if you do not need the this reference in the second handler, use handler2(e); instead.
further readings:
Function.prototype.call
Here is a half-baked demo passing the mouse event args. I'm unsure how well supported layerX/Y is, I just used it to show the demo.
I have an anchor tag on my page, I want an event attached to it, which will fire when the display of this element change.
How can I write this event, and catch whenever the display of this element changes?
This is my way of doing on onShow, as a jQuery plugin. It may or may not perform exactly what you are doing, however.
(function($){
$.fn.extend({
onShow: function(callback, unbind){
return this.each(function(){
var _this = this;
var bindopt = (unbind==undefined)?true:unbind;
if($.isFunction(callback)){
if($(_this).is(':hidden')){
var checkVis = function(){
if($(_this).is(':visible')){
callback.call(_this);
if(bindopt){
$('body').unbind('click keyup keydown', checkVis);
}
}
}
$('body').bind('click keyup keydown', checkVis);
}
else{
callback.call(_this);
}
}
});
}
});
})(jQuery);
You can call this inside the $(document).ready() function and use a callback to fire when the element is shown, as so.
$(document).ready(function(){
$('#myelement').onShow(function(){
alert('this element is now shown');
});
});
It works by binding a click, keyup, and keydown event to the body to check if the element is shown, because these events are most likely to cause an element to be shown and are very frequently performed by the user. This may not be extremely elegant but gets the job done. Also, once the element is shown, these events are unbinded from the body as to not keep firing and slowing down performance.
You can't get an onshow event directly in JavaScript. Do remember that the following methods are non-standard.
IN IE you can use
onpropertychange event
Fires after the property of an element
changes
and for Mozilla
you can use
watch
Watches for a property to be assigned
a value and runs a function when that
occurs.
You could also override jQuery's default show method:
var orgShow = $.fn.show;
$.fn.show = function()
{
$(this).trigger( 'myOnShowEvent' );
orgShow.apply( this, arguments );
return this;
}
Now just bind your code to the event:
$('#foo').bind( "myOnShowEvent", function()
{
console.log( "SHOWN!" )
});
The code from this link worked for me: http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/
(function ($) {
$.each(['show', 'hide'], function (i, ev) {
var el = $.fn[ev];
$.fn[ev] = function () {
this.trigger(ev);
return el.apply(this, arguments);
};
});
})(jQuery);
$('#foo').on('show', function() {
console.log('#foo is now visible');
});
$('#foo').on('hide', function() {
console.log('#foo is hidden');
});
However the callback function gets called first and then the element is shown/hidden. So if you have some operation related to the same selector and it needs to be done after being shown or hidden, the temporary fix is to add a timeout for few milliseconds.