Jquery .clone can't set named function as event handler - javascript

I'm trying to create a method that will copy the HTML from another part of the page and then set a click handler for the elements inside of it. Problem I'm having is that the handler is not firing when clicked.
Its funny because when I set the handler as an anonymous function, it fires when clicked, but when I set it to either self.initSizeSZ or myModule.initSizeSZ it won't fire.
// Elements are cached for performance.
var $selectSizes = $('#someSelector');
var $catalogWheels = $('#someOtherSelector');
var myModule = {
// This is the module that calls renderSizeSZ
loadWheels: function(){
if(!this.loaded){
this.renderSizesSZ();
this.loaded = true;
}
},
renderSizesSZ: function(){
var self = this;
var $clone = $selectSizes.clone(false);
var html = $selectSizes.html();
$catalogWheels
.find('#icf_catalog-sizes')
.html(html)
.find('li a')
// if this is set as an anonymous function, it will fire, but as a named function it won't
.click(self.initSizeSZ);
},
initSizeSZ: function(event){
console.log('firing')
}
}

Related

React callback click event `this` undefined

I am using d3 and react to create charts. Clicking a button will update the chart. In the component for the chart is an onclick event listener, however the "this" keeps getting mixed up
One method with arrow functions returns Uncaught TypeError: node.getAttribute is not a function:
this.setupButtons= function() {
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', ()=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
var buttonId = button.attr('id');
this.toggleDisplay(buttonId)
});
}
*/
This is of course because the this refers to the component instance. So I researched how to refer to the click event and found that using e or event.target is supposed to solve the issue. In react, however, an error is returned: Uncaught TypeError: Cannot read property 'target' of undefined
this.setupButtons= function() {
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', (e)=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(e.target);
button.classed('active', true);
var buttonId = button.attr('id');
this.toggleDisplay(buttonId)
});
}
Both this.setupButtons and this.toggleDisplay() are defined in the same method, which belongs to a component.
EDIT: This question does not appear to be a "duplicate" of the question provided.This was an issue of D3's handling of event, not the use of this, apparently. The solution to this question (add d3.event.target instead of event.target) is not provided as an answer in the question this supposedly duplicates.
You can store the reference of the outer this into another variable. I generally store it into a variable like var that = this and then you can reference the variable that wherever required.
this.setupButtons= function() {
var that = this;
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', ()=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(that);
button.classed('active', true);
var buttonId = button.attr('id');
that.toggleDisplay(buttonId)
});
}
d3 goes about things a bit differently. The event is accessed from d3 itself (d3.event).
Source: https://octoperf.com/blog/2018/04/17/d3-js-mouse-events-and-transitions-tutorial/#how-to-use-d3-event-object
Assign var that = this in the outer function. Now you have a way to reference the outer this.
this.setupButtons = function () {
var that = this;
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', function() {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
var buttonId = button.attr('id');
that.toggleDisplay(buttonId)
});
}

Using bind function in event listeners

I have been writing a plugin, and i really like this format
Function.prototype._onClick = function() {
// do something
}
Fuction.prototype.addListner = function() {
this.$element.on('click', this._onClick.bind(this));
}
the problem is sometimes i need the element being clicked and the main object. Doing as below i loose the dom element and not using bind looses the main object.
Fuction.prototype.addListner {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
To achieve that i go back to ugly version
Fuction.prototype.addListner = function() {
var self = this;
this.$element.find('.some-class').on('click', function() {
self._onClick($(this));
});
}
Is there any better way to do this?
As zerkms, you can use the event.target to achieve what you want.
When using .on, the handler is :
handler
Type: Function( Event eventObject [, Anything extraParameter ] [, ...
] ) A function to execute when the event is triggered. The value false
is also allowed as a shorthand for a function that simply does return
false.
So your _onClick function will receive click event as its 1st parameter, then from event.target, you can now get the clicked item.
var Test = function(sel) {
this.$element = $(sel);
this.value = 'My value is ' + this.$element.data('val');
};
Test.prototype.addListner = function() {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
Test.prototype._onClick = function(evt) {
// Get the target which is being clicked.
var $taget = $(evt.target);
//
console.log(this.value);
// use $target to get the clicke item.
console.log($taget.data('val'));
}
var test = new Test('#test');
test.addListner();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<div id="test" data-val="divVal">
<button class="some-class" data-val="Button-A">btnA</button>
<button class="some-class" data-val="Button-B">btnB</button>
</div>

convert javascript event handler to jquery

I would like to convert the event handler to a jquery style click event but it doesnt seem to like passing the event through, perhaps its because its not an anonymous function anymore?
// variables
var faqOne = document.getElementById("faqOne");
var $hiddenOne = $(".faqOneHidden");
// javascript event handler works!
faqOne.addEventListener("click", function(e){
showFaqOne.showClickedFaq(e);
}, false);
// javascript event handle - doesnt work!
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
// constructor
function DisplayQFaqs(link, faq){
this.link = link;
this.faq = faq;
}
// method prototype
DisplayQFaqs.prototype.showClickedFaq = function(e){
var el = e.currentTarget;
if(el === this.link) {
this.faq.toggle("slow", function(){
});
}
};
// new DisplayQFaqs Objects
var showFaqOne = new DisplayQFaqs(faqOne,$hiddenOne);
Your e is undefined inside
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
Change it to
$("#faqOne").click(function(e){//Now e is there
showFaqOne.showClickedFaq(e);
});

How to mock/stub callback function with sinon.js?

I want to write unit tests with QUnit and Sinon.Js. I have an application, where the user can click on a button and a modal dialog appers to handle downloading some files. The user can close the dialog and it triggers a method to run to reset some variables. My test code:
$(function() {
$.fn.copy_button = function(){};
ln_download_view = new DownloadModalView();
ln_download_view.modal = {'modal': function() {}};
var download_modal_dialog = $('.download-modal');
download_modal_dialog.modal = function(param){};
var modal_mock = sinon.mock(ln_download_view.modal);
var download_modal_dialog_mock = sinon.mock(download_modal_dialog);
//Should be inserted, because ln_download_view.modal is mocked
//The close button even handler
$('#btn_close_modal').click(function(){
download_modal_dialog.modal('hide');
});
//Dirty stuff to do after the window closes
//Basicly the click triggers this event handler
$('.download-modal').on('hide',function() {
window.clearInterval(window.periodicalTimer);
});
$('div .option-container').click(function() {
if(!$(this).hasClass("selected-option"))
{
$('div #option-presenting').toggleClass("selected-option");
$('div #option-editing-and-presenting').toggleClass("selected-option");
$('.image').toggle();
}
});
module("views");
test("Download modal dialog is displayed", function(){
var modal_triggered = modal_mock.expects("modal").once();
ln_download_view.handleDownloadClick();
ok(modal_triggered.verify());
});
test("Download modal dialog is closed",function(){
var modal_triggered = download_modal_dialog_mock.expects("modal");
$('#btn_close_modal').trigger('click');
ok(modal_triggered.verify());
});
});
What I do not understand is, how can I test/mock/stub this piece of code:
$('.download-modal').on('hide',function() {
window.clearInterval(window.periodicalTimer);
});
I do not have the deep understanding yet.
You can't mock/stub an anonymous function. But you can make a refactoring and stub/mock the named callback.
$('.download-modal').on('hide', onHide);
var onHide = function() {
window.clearInterval(window.periodicalTimer);
};
// ...
sinon.stub(onHide);
Here's my method for this:
In your before each, make a function that doesn't do anything:
var doNothing = function(){};
Then in your test, spy on that:
var spy = sinon.spy(this, 'doNothing');
Then call your method, passing in a callback that fires the doNothing method:
var self = this;
whatever.doSomethingAwesome(
{
finished: function(){
self.doNothing();
}
});
expect(spy.callCount).toEqual(1);

How to dynamically register to jquery custom events?

I have built a dom object Engine that has private/public fields/methods that I have simplified below:
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
}
}
What I would like to do is build a custom event that would be triggered after the alert(msg) such as $(this.display).trigger("afterAlert");
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
// trigger custom event here
$(this.display).trigger("afterAlert");
}
}
Now, this event could be empty or not. How would one or more objects declared later register to the "afterAlert" event? In my case, additional javascript files are loaded by the main file dynamically and could contain a code ressembling :
function new_obj(){
bind("afterAlert", function(){
alert("alert was called");
});
}
See my answer from this question...repeated for clarity
I will tackle the register, triggering and unbinding of custom events.
jQuery has all the tools you need to register, bind and unbind to custom events.
Below is an example of hooking up two divs to a custom event called customAjaxStart. I can then trigger this function and both handlers will get called.
Quick Demo Here - Have the firebug/ie8 console enabled.
e.g
$( function() {
$('#div1').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
$('#div2').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
//fire the custom event
$.event.trigger('customAjaxStart');
//unbind div1 from custom event
$('#div1').unbind('customAjaxStart');
//again trigger custom event - div1 handler will not fire this time
$.event.trigger('customAjaxStart');
});
Taking the above as an example I would trigger the customAjaxStart from the global ajaxStart. Any listeners would be triggered automatically whenever an xhr call is about to be made (ideal for disabling your widgets or showing a loading gif etc) e.g
$.ajaxStart( function(){
$.event.trigger('customAjaxStart');
});
I think what you are looking for is the Observer pattern. At least that's how I would implement it. The following code snippet uses different names but it does essentially what you want (allows registering for events, and even triggering):
Observable = {
addObserver: function(observer) {
if (!this.__observers) this.__observers = [];
this.__observers.push(observer);
},
addGlobalObserver: function(observer) {
if (!this.__global_observers) this.__global_observers = [];
this.__global_observers.push(observer);
},
removeObserver: function(observer) {
var newObservers = [];
var co;
while (co = this.__observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__observers = newObservers;
newObservers = [];
while (co = this.__global_observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__global_observers = newObservers;
},
notify: function(event) {
var allObservers = this.__global_observers.concat(this.__observers);
for (var i=0; i < allObservers.length; i++) {
var o = allObservers[i];
if (o[event]) {
var args = []
for (var j=1; j < arguments.length; j++) {
args.push(arguments[j])
};
o[event].apply(this, args);
}
};
},
__global_observers: [],
__initializer: function() {
this.__observers = [];
}
};
If you include this code into your class, you can register for events using addObserver() (addGlobalObserver() for "class level" events). Inside the object you trigger an event using notify().
Code taken from Coltrane.

Categories