Bind event relative to the class that created it? - javascript

Let's say I have a class laid out like so:
function slider() {
this.init = function(options, title, content) {
var self = this;
$('body').append('<button type="button">' + title + '</button>')
},
this.create = function(title, content, options) {
var self = this;
self.init(options, title, content);
},
this.closeSlider = function(elem) {
var self = this;
self.assignPositions();
},
this.assignPositions = function() {
alert('assign positions called from button?');
}
}
To create the 'slider', I use this:
var slider = new slider();
Then I call the create function:
slider.create('title', 'content');
My question is, how can I bind the closeSlider function to the button, but it's only linked to the instance that created it? If that makes sense?
Basically, I'll have many buttons with the 'closeSlider' function, and I don't want them all to fire at once, I only want it linked to the instance that created it.
This is also a VERY trimmed down version of my class, just trying to figure this little problem out :)
Cheers

Use bind on the callback function when setting the event callback
this.init = function(options, title, content) {
var btn = $('<button type="button">' + title + '</button>');
btn.click(this.closeSlider.bind(this));
$('body').append(btn)
},
This will make it so when the closeSlider function is called it retains the context of the slider instance that made it. But note this will no longer be the context of the html element that triggered the event. So you would need to get the target from event.target
There might be another way of doing this without losing the context of the html element i will have to look and re-edit.
Edit
Using event.target
this.init = function(options, title, content) {
var btn = $('<button type="button">' + title + '</button>');
btn.click(this.closeSlider.bind(this));
$('body').append(btn)
},
this.closeSlider:function(event){
//`this` will refer to slider instance
//and event.target will be the button dom object
var element = event.target;
});
Passing the button object as an argument in bind
this.init = function(options, title, content) {
var btn = $('<button type="button">' + title + '</button>');
btn.click(this.closeSlider.bind(this,btn));
$('body').append(btn)
},
this.closeSlider:function(btn,event){
//`this` will refer to slider instance
//btn will refer to the jQuery wrapped button dom object
//event.target will still refer to the button dom object
});

Instead of simply appending a string to the body, create a live element with document.createElement and attach the onclick event before releasing it into the wild.
Here's an example:
function slider() {
this.init = function(options, title, content) {
var myButton = document.createElement("button");
myButton.setAttribute("type", "button");
myButton.innerHTML = title;
myButton.onclick = this.closeSlider;
$('body').append(myButton);
},
//Other object definitions
}

First of all, you should add methods onto the prototype rather than on each instance (saves memory).
Secondly, you can use jQuery's .proxy() to create an anonymous function that will "hardwire" this to a particular value when it calls your method.
// empty constructor
function slider()
{
}
// define prototype
$.extend(slider.prototype, {
init: function(options, title, content) {
$('<button>', { text: title })
.on('click', $.proxy(this, 'closeSlider'))
.appendTo('body');
},
create: function(title, content, options) {
this.init(options, title, content);
},
closeSlider: function(event) {
// event.target is the HTML element
this.assignPositions(event.target);
},
assignPositions: function(elem) {
alert('assign positions called from button?');
}
});

Related

JointJS element containing HTML button onclick show form

I am showing a form on addDetail buttton inside this element. How can I bind my data to this cell and send it to the server using the toJSon() method?
// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------
joint.shapes.html.ElementView = joint.dia.ElementView.extend({
template: [
'<div class="html-element">',
'<button class="delete">x</button>',
'<label></label>',
'<span></span>', '<br/>',
'<input type="text" name="name" placeholder="name"/>',
'<button class="addDetail">+</button>',
'</div>'
].join(''),
initialize: function () {
_.bindAll(this, 'updateBox');
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
this.$box = $(_.template(this.template)());
// Prevent paper from handling pointerdown.
this.$box.find('input').on('mousedown click', function (evt) {
evt.stopPropagation();
});
// This is an example of reacting on the input change and storing the input data in the cell model.
this.$box.find('input').on('change', _.bind(function (evt) {
alert($(evt.target).val());
this.model.set('input', $(evt.target).val());
}, this));
this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
this.$box.find('.addDetail').on('click', _.bind(function (evt) {
addActionDetail();
})
);
// Update the box position whenever the underlying model changes.
this.model.on('change', this.updateBox, this);
// Remove the box when the model gets removed from the graph.
this.model.on('remove', this.removeBox, this);
this.updateBox();
},
render: function () {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.paper.$el.prepend(this.$box);
this.updateBox();
return this;
},
updateBox: function () {
// Set the position and dimension of the box so that it covers the JointJS element.
var bbox = this.model.getBBox();
// Example of updating the HTML with a data stored in the cell model.
this.$box.find('label').text(this.model.get('label'));
this.$box.find('span').text(this.model.get('select'));
this.$box.css({
width: bbox.width,
height: bbox.height,
left: bbox.x,
top: bbox.y,
transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)'
});
},
removeBox: function (evt) {
this.$box.remove();
}
});
}
In order to save some data on your element you must follow this steps:
Add some elementData propery to the shape model.
Each time the user click on addDetail inside your element you must have the element id, extract the elementData out of it, and then to render the form (you can achieve this by adding custom event listener to your paper)
When clicking the submit form, add add some custom trigger event.
Listen to that triggered event on your graph and try look for the specific cell by the ModelId and update it.
Here is the basic idea example:
1.your shape model:
joint.shapes.myShapes = joint.shapes.myShapes || {};
joint.shapes.myShapes.Element = joint.shapes.basic.Generic.extend({
//basically the defaults function doesn't needed, because the set function adds that automatically.
defaults: _.defaultsDeep({
elementData: null,
}, joint.shapes.basic.Generic.prototype.defaults),
getElementData: function () {
return this.get("elementData");
},
setElementData: function (elementData) {
this.set("elementData", elementData);
},
});
2.On your paper init, add your custom event listener function,
notice that you must have the ModelId to be remembered:
paper.on('addDetail:click', function (cell) {
var elementData = cell.model.getElementData();
elementData.ModelId = cell.model.id;
formRender(elementData);
});
3.trigger some custom event on your submit and the object to be updated within the element model:
function formSubmit() {
graph.trigger('custom:update', newElementData);
}
4.Add some custom event listener to your graph, add call the setElementData by the ModelId:
graph.on('custom:update', function (elementData) {
var cell = graph.getCell(elementData.ModelId);
cell.setElementData(elementData);
}, this);
Now you can send it to the server using the toJSon() method.

Converting Jquery code to vanilla JS, option value selction and 'one' method not working

I want to have a Jquery function converted to JS and I am having trouble implementing it.
The Jquery code is commented out, it works exactly as it should.
The main function takes a parameter with the selected option from the select menu(make sure to pick the LeftAndZoom option) and then applies the chosen animation to the container.
This is the main function, I simply cannot figure out how to convert this to JS.
$('#imageContainer').removeClass().addClass(x + ' animation').one
('webkitAnimationEnd mozAnimationEnd MSAnimationEnd
oanimationend animationend', function(){
$(this).removeClass();
});
This is the JS I have so far, I've tried many things, but to no avail yet.
function testAnim(x) {
var container = document.getElementById("imageContainer").
container.className = "";
container.classList.add(x + ' animation');
this.container.className = "";
}
I know this cannot possibly work, but it's still better than what I've tried.
And this is the accompaigning function, to extract the parameter value from the options list:
$(document).ready(function(){
$('.triggerAnimation').click(function(e){
e.preventDefault();
var anim = $('.jsanimations').val();
testAnim(anim);
});
$('.js--animations').change(function(){
var anim = $(this).val();
testAnim(anim);
});
});
My JS for this is:
var trigger = document.querySelector(".triggerAnimation");
var JSanimation = document.querySelector(".js--animations");
trigger.addEventListener('click', function(e){
e.preventDefault();
var anim = JSanimation.options[this.selectedIndex].innerText;
testAnim(anim);
JSanimation.addEventListener("change", function(){
var anim = this.options[this.selectedIndex].innerText;
});
});
Main issue here is, I don't know how to implement the JS equivalent 'one' method, how to keep "this" context and how to add a callback to the 'one' method
And on top of that, i get an error "Uncaught TypeError: Cannot read property 'options' of null"
Looks like the selection of the option value is not working either.
Link to pen:
http://codepen.io/damianocel/pen/KdobyK
As for making a "one" type of function, here's one way:
function bindOne(el, evtTypes, callback, captures) {
var allEvents = evtTypes.trim().split(/\s+/)
allEvents.forEach(function(evtType) {
el.addEventListener(evtType, function boundFn() {
this.removeEventListener(evtType, boundFn, captures)
callback.call(this, event)
}, captures)
})
}
The function defines 4 parameters...
el is the element to be bound
evtTypes is a space separated string of event types to be bound
callback is your event handler
captures indicates if you want bubbling or capturing for this event. You can usually just leave that one off.
Instead of binding your callback directly, it binds a function that immediately unbinds itself on the first event, and then manually invokes your original callback.
The way it keeps the proper this value is that it invokes your callback using .call(), which lets you manually set the value of this in the function being called.
So then you use it like this:
function testAnim(x) {
var el = document.querySelector("#imageContainer")
el.className = ""
el.classList.add(x + ' animation')
bindOne(el, 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd' +
' oanimationend animationend', function() {
this.className = ""
});
}
Your TypeError is simply because there is no js--animations class in your HTML. Should be jsanimations.
Also, I would probably make your bindOne handle browser prefixes for you. That way you only need to pass 'animationend', and your function can unbind them all as well.
var needsPrefixes = {
animationend: true
// can add others as needed
}
var prefixes = ["webkit", "moz", "ms", "o"]
function bindOne(el, evtTypes, callback, captures) {
var allEvents = evtTypes.trim().split(/\s+/)
allEvents.forEach(function(evtType) {
var doPrefixes = needsPrefixes[evtType.toLowerCase()]
if (doPrefixes) {
prefixes.forEach(function(prefix) {
el.addEventListener(prefix + evtType, boundFn, captures)
})
}
el.addEventListener(evtType, boundFn, captures)
function boundFn() {
if (doPrefixes) {
prefixes.forEach(function(prefix) {
this.removeEventListener(prefix + evtType, boundFn, captures)
}, this)
}
this.removeEventListener(evtType, boundFn, captures)
callback.call(this, event)
}
})
}

How bind a event and use apply/call to change the scope

+function ($) {
'use strict';
var popup = {
init: function(element) {
this._active = 'products__popup--active';
this._product = $('.products__popup');
this._element = $('[data-popup-to]');
this._TIME = 500;
popup.attachEvt();
},
attachEvt: function() {
var that = this;
that._element.bind('click', popup.handlerEvt.call(that));
},
handlerEvt: function() {
console.log(this);
console.log('test');
}
};
$(window).on('load', function() {
popup.init();
});
}(jQuery);
I have this script, and is not working yet, I cant show you a working example because it is not ready, I'm organizing the code first.
And there is a problem with the attachEvt function, inside it I want to call another function of my object, this function will bind a click in the that._element, but I want pass to the handlerEvt the scope of this (the clicked element) and the that (the object), but this is not working:
that._element.bind('click', popup.handlerEvt.call(that));
I'm just passing the that scope and when the script loads, the element will be clicked without click, I want avoid this.. this is possible?
UPDATE:
Resuming:
I want be able to use the scope of the object (that) and the scope of the clicked element (this) inside the handlerEvt function, but without make the event click when the script loads.. :B
Try utilizing .bind() , with this set to that._element , that passed as parameter to handlerEvent . Note order of parameters at handlerEvent: obj: that first , evt event object second
+function ($) {
'use strict';
var popup = {
init: function(element) {
this._active = 'products__popup--active';
this._product = $('.products__popup');
this._element = $('[data-popup-to]');
this._TIME = 500;
popup.attachEvt();
},
attachEvt: function() {
var that = this;
that._element.bind('click', popup.handlerEvt.bind(that._element, that));
},
handlerEvt: function(obj, evt) {
console.log(evt, obj, this);
console.log('test');
}
};
$(window).on('load', function() {
popup.init();
});
}(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div data-popup-to="true">click</div>

change loses bind reference

Have a link that uses the ID to grab specific content from an external file (works, no problems here). I then change the ID of the link so that a new ID so that new info can be gotten the from the external file but the same info is always shown. So, I'm looking for some help in figuring out why the new info isn't being loaded but the old instead.
Here is the code for setting the new ID value. It resides in a completely separate section of the code. It then calls the function fncSetupInfoWindow() and it creates the bindings. .windowInfo is a class set on specific elements.
$('#pickedCenterProgram a').text(newProgram[0]).parent().slideDown().attr('id', newVal).attr('title', newProgram);
fncSetupInfoWindow();
function fncSetupInfoWindow() {
$('.windowInfo').unbind('mouseover mouseout').each(function () {
var obj = $(this), position = [], contentID = globalObjects.emptyString, title = obj.attr('title'), contentID = obj.attr('id').toLowerCase();
obj.bind({
mouseover: function (e) {
position = fncGetPositionArray(e.pageX, e.pageY);
fncLoadStatusInfo(position[0], position[1], contentID, title);
},
mouseout: function () {
$('#modInfoWindow').closeModal();
}
});
});
}
Here is the code for loading the info
function fncLoadStatusInfo(x, y, programName, title) {
initLoader.className = 'txt-c'; //create loader
initLoader.iconClass = 'alignVert-b';
$('#modInfoWindow').createModalWindow({ isModal: false, left: x, top: y, ignorePosition: false, title: title, width: 250, hideCloseIcon: true, autoOpen: true }).html(initLoader.createLoader());
$('#modInfoWindow').load('../pages/infoFile.htm ' + '#' + programName);
return false;
}
Everything works, well almost, except that the newly assigned ID is not being used but the original for when the page is created. I've tried numerous things as well as even destroying the modal (aka dialog) window all with the same results.
thanks ^.^
Thanks.
The problem is that you're using the id at the time it was bound (inside the .each()), rather than at the time of the event (inside the event handler). You can do it at the time of the event and simplify things overall like this:
function fncSetupInfoWindow() {
$('.windowInfo').bind({
mouseover: function (e) {
var pos = fncGetPositionArray(e.pageX, e.pageY);
fncLoadStatusInfo(pos[0], pos[1], this.id.toLowerCase(), this.title);
},
mouseout: function () {
$('#modInfoWindow').closeModal();
}
});
}
Or even simpler (probably what you want) using .hover():
function fncSetupInfoWindow() {
$('.windowInfo').hover(function (e) {
var pos = fncGetPositionArray(e.pageX, e.pageY);
fncLoadStatusInfo(pos[0], pos[1], this.id.toLowerCase(), this.title);
}, function () {
$('#modInfoWindow').closeModal();
});
}

Dynamically call method of a class with javascript

I want to dynamically call the method of a custom class much like the below javascript. Except, the javascript below only calls a function that exists in my code. I want to call (dynamically) the function of a class. So I would remove window{value](target, event, self); and use something else that would call the method of a custom created class such as "mycustomclass.anythingcouldbethismethod(target, event, self);" after it had been instantiated of course.
var functions = [
'ajaxify_overlay',
'ajaxify_overlayCancel',
'ajaxify_overlaySubmit',
'ajaxify_rollout',
'ajaxify_rolloutCancel',
'ajaxify_rolloutSubmit',
'ajaxify_upload',
'ajaxify_contentArea',
'ajaxify_itemToggler',
'ajaxify_closer',
'ajaxify_submit',
'ajaxify_inputActivate',
'ajaxify_executeAndRefresh',
'ajaxify_empty'
];
$(document).bind('ready', function(event) {
$('body').live('click', function (event){
var target = $(event.target);
var self = this;
$.each(functions, function(index, value){
if($(target).hasClass(value)) {
window[value](target, event, self);
}
});
});
});
var myClass = { /* your class definition */ };
var methodName = 'myMethod';
myClass[methodName](p1,p2,...,pN);
You mean like this?
function methodCaller( methodName, target, event, self ) {
mycustomclass[ methodName ](target, event, self);
}
methodCaller( "someMethodName" );

Categories