I'm trying to bind a function to the anchor onclick attribute. I'm not using the traditional jQuery's bind/live/on/whatever because I have some other scripts stopping the events propagation (it sucks, I know).
To bind the function to the onclick attribute I'm passing a JSON object to a module like this:
function foo() {
alert('foo')
}
$('document').ready(function() {
var options = {
opt1: 'fooID',
opt2: 'barID',
json: mightyJSON,
actions: [
{ url: 'contact/_id_/edit', text: "Edit", iconPath: 'edit.png' },
{ url: '#', onClick: foo, text: "Delete", iconPath: 'delete.png' }
]
};
var trolol = myModule.configure(options);
});
As you can see the function named "foo" is passed via the onClick property of the JSON. The function is defined above the object.
In myModule I'm creating the anchor tag like this:
var buildLinks = function(objectID)
{
var linksNbr = actions.length;
var link, cssClass;
for (var i = 0; i < linksNbr; i++)
{
// Adding the object ID to the URL
link = actions[i].url.replace('_id_', objectID);
cssClass = actions[i].cssClass || '';
var $link = $(document.createElement('a')).attr('onClick', actions[i].onClick)
.attr('href', link)
.attr('title', actions[i].text)
.addClass(cssClass)
.text('foo');
}
return $link.html();
};
The thing is, as you can expect 'foo' is executed when the script is parsed and only there. The onclick doesn't even work after.
I can pass it like this onClick: 'foo()'. The onclick works but it's also executed at parsing and it's, in my opinion, very ugly.
I'd like to still be able to pass it like this onClick: foo but working correctly (i.e. not being executed at loading but only when clicking.
It has to work with jQuery 1.4.4 unfortunately.
I would do it like this:
var $link = $('<a></a>',{
href : link,
title : actions[i].text,
'class' : cssClass,
text : 'foo',
click : actions[i].onclick
})
return $link;
Then use one of these (1,2) functions to insert the node, which is the html with events.
for the propagation issue i would do something like this:
html <a href="#no" ....>text</a>
js $('a[href="#no"]').live('click',function(){ return false; });
This way whenever the href is pointing to #no the event is eventually propagated
if at all possible, return the element, and not its .html()
having done that, don't use .attr('onclick', ...) when you've already got a function reference, use .prop, or even just element.onclick = ...
e.g.
$link = $('<a>', {
href: link,
title: actions[i].text,
'class': cssClass,
text: 'foo'
}).prop('onclick', actions[i].onClick);
Here is a fiddle snippet. If this approach is fine, you could as below set onclick on jquery's raw element, like this:
$link[0].onclick = options.actions[i].onClick;
Related
I am using TinyMCE 4 and trying to build a dynamic menu. In order to do this I am building an array of menu items which includes an onclick function. The menu displays, but the onclick function does not work because when building the array, the value I need to pass to the function is out of scope - I believe.
var MenuItems = [{"Id":"1","Name":"MenuItem 1"},{"Id":"2","Name":"MenuItem 2"}];
var Menu = [];
for (var i=0;i<MenuItems.length;i++)
{
Menu.push({
text: MenuItems[i].Name,
onclick: function(){
alert(MenuItems[i].Id);
}
});
}
In the onclick declaration, MenuItems[i].Id is not in scope - I believe.
How can I pass the values to the onclick function.
I am then passing the array to the TinyMCE plugin, but I don't believe this is a problem with TinyMCE, but posting this part in case there is a better way.
tinymce.PluginManager.add('myplugin', function(editor, url) {
editor.addButton('menu', {
text: 'MyMenu',
type: 'menubutton',
icon: false,
menu: Menu
});
});
MenuItems[] won't be available when the callback for myplugin would run.
This would also mean, that once, onclick of any menuItem is called, it would try accessing MenuItems[].
To fix this, once way could be to change the implementation like:
var MenuItems = [{"Id":"1","Name":"MenuItem 1"},{"Id":"2","Name":"MenuItem 2"}];
var Menu = [];
for (var i=0;i<MenuItems.length;i++)
{
const id = MenuItems[i].Id;
Menu.push({
text: MenuItems[i].Name,
onclick: function(){
alert(id);
}
});
}
I want to execute a local method in Ext.Template context.
The method should be a member in the class.
I tried the following code and it doesn't work.
Someone know of can I pass the function member to onClick event?
requires: ['Ext.XTemplate'],
alias : 'widget.countlinkcolumn',
func: 'this.handleFilter'
renderer: function(val,metaData,rec,b,c,d,f){
var categoryId = 3;
var colTemplate = new Ext.Template(
'<div class="drill_down_link grid_cell_link" style="cursor: pointer; float:right" onclick="{on_click}({categoryId})">{text}</div>' +
'</div>');
var tpl = colTemplate.apply({
text: text,
categoryId: categoryId,
on_click: this.func,
});
return tpl;
},
handleFilter: function (categoryId) {
console.log(categoryId);
},
});
Never found an simple solution to this problem... The XTemplate can't directly call ExtJS code from it (it's actually already rendered in the DOM)...
The workaround we found is to render the XTemplate in a View (But you can do it with apply()) then listening to the itemClick event.
In the listener we get the DOM element and we can get some additional data from an attribute (eg: data-categoryId):
xtype: 'view',
listeners: {
itemClick: 'onItemClick',
}
// Additional attribute (data-categoryId) that store the categoryId
tpl: '<div data-categoryId="{categoryId}" class="drill_down_link">{text}</div>'
Then in the listener we can use this additional attribute after we checked that the correct button was clicked (by his class name but you can use other attribute)
onItemClick: function(dataView, record, item, index, e, removeAll){
var me = this,
target = e && e.target,
targetClass = target && target.getAttribute("class");
//Clicked on link (identified by his class name)
var isLink = targetClass && targetClass.indexOf("drill_down_link") >= 0;
if(isLink){
// Get the attribute value we setted in the XTemplate
var categoryId = target.getAttribute('data-categoryId');
}
}
Now that I understand Backbone a little better (I Hope) I've been going through this App with a fine tooth comb to understand how it works:
https://github.com/ccoenraets/nodecellar/tree/master/public
The latest thing that's stumped me is the EL tag in windetails.js (here: https://github.com/ccoenraets/nodecellar/blob/master/public/js/views/winedetails.js)
I'll paste the relevant code below, but my question is how does this view's EL property get assigned? As you'll notice in the view definition no EL tag is defined, nor is there an idTag or className property assigned. However I verified in firebug that this view is indeed listening on a DIV tag in the middle of the DOM (just underneath the content DIV actually). So how did it get attached there? If not for that the Click handler would not work properly but it does. All of the previous views which look like there were created in the same way have unattached EL properties.
window.WineView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
events: {
"change" : "change",
"click .save" : "beforeSave",
"click .delete" : "deleteWine",
"drop #picture" : "dropHandler"
},
change: function (event) {
// Remove any existing alert message
utils.hideAlert();
// Apply the change to the model
var target = event.target;
var change = {};
change[target.name] = target.value;
this.model.set(change);
// Run validation rule (if any) on changed item
var check = this.model.validateItem(target.id);
if (check.isValid === false) {
utils.addValidationError(target.id, check.message);
} else {
utils.removeValidationError(target.id);
}
},
beforeSave: function () {
var self = this;
var check = this.model.validateAll();
if (check.isValid === false) {
utils.displayValidationErrors(check.messages);
return false;
}
this.saveWine();
return false;
},
saveWine: function () {
var self = this;
console.log('before save');
this.model.save(null, {
success: function (model) {
self.render();
app.navigate('wines/' + model.id, false);
utils.showAlert('Success!', 'Wine saved successfully', 'alert-success');
},
error: function () {
utils.showAlert('Error', 'An error occurred while trying to delete this item', 'alert-error');
}
});
},
deleteWine: function () {
this.model.destroy({
success: function () {
alert('Wine deleted successfully');
window.history.back();
}
});
return false;
},
dropHandler: function (event) {
event.stopPropagation();
event.preventDefault();
var e = event.originalEvent;
e.dataTransfer.dropEffect = 'copy';
this.pictureFile = e.dataTransfer.files[0];
// Read the image file from the local file system and display it in the img tag
var reader = new FileReader();
reader.onloadend = function () {
$('#picture').attr('src', reader.result);
};
reader.readAsDataURL(this.pictureFile);
}
});
EDIT
There's been a lot of talk about this pattern:
$(x).append(v.render().el)
Someone correct me if I'm wrong but as I understand it this is a Jquery call to update the DOM at the "x" tag with the contents of the "el" property from the v object (after render is called). This technique should render content into the DOM EVEN IF the "el" property has not previously been set and is an "unattached div" provided it has had valid content previously written to it from the render method.
However after the content has been written to the DOM the "el" property still remains an unattached div until it is directly assigned to the DOM.
I verified through Firebug that this Backbone app has two views which are rendered this exact way and both have unattached div el properties. Those are the wineList view and the homeView. However, the 3rd view is the WineDetail view and it does not seem to have an unattached EL property. It's EL property seems to be attached and furthermore is facilitating a click event. My question is how did this EL property get attached and assigned to the DOM?
The answer can be found by looking at the internals of Backbone.View.
Looking at the constructor:
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
this._configure(options || {});
//this function is responsible for the creation of the `this.el` property.
this._ensureElement();
this.initialize.apply(this, arguments);
this.delegateEvents();
};
Ensure that the View has a DOM element to render into. If this.el is a
string, pass it through $(), take the first matching element, and
re-assign it to el. Otherwise, create an element from the id,
className and tagName properties. http://backbonejs.org/docs/backbone.html#section-133
Now that we know where this.el comes from, have a look at the events docs to see how it's handled.
The view is instantiated in main.js
$('#content').html(new WineView({model: wine}).el);
EDIT:
None of which explains how the View Object's EL property is set and
and how the click trigger works.
I will try to explain it better:
this.el is created by a call to this._ensureElement in the Backbone.View constructor. We can also see that this.render is called from the initialize function which runs at instanciation time. We can see that in this.render, we set the content of this.el to the result of applying this.template to the model.
Now, during the initialization process of a Backbone.View, right after this.initialize is called, the events config is processed by making a call to this.delegateEvents. This is where event listeners will get attached using the given selectors. Note that most events will get attached directly to this.el and make use of event delegation, instead of attaching the events directly on the children elements.
At this point, we are left with a this.el that contains all the necessary markup and has all the event listeners setup. However, this.el is still not part of the DOM yet.
But from the code, we can see that this.el will be attached to the DOM as a children of the #content element after the instanciation of the view:
$('#content').html(new WineView({model: wine}).el);
The last three lines in this piece of code:
events: {
"change" : "change",
"click .save" : "beforeSave",
"click .delete" : "deleteWine",
"drop #picture" : "dropHandler"
},
look like this pattern (looking at the 2nd line in the events structure):
"click" = event to register a handler for
".save" = selector to use for selecting objects for the event handler
beforeSave = method to call when the event fires
I am creating a link that changes text when it is clicked. I want the link text to change back to the original text after all the processing is complete. It was working fine, but the code was spread all over my js file, so I am trying to abstract it into a function. This is the function, textToggle. In the textToggle function we are publishing an event. This event is the one that I cannot get to fire off at the right time.
var textToggle = function(data) {
var original_text = $(data.element).text();
var id = data.id;
var $element = $(data.element);
$element.text(data.replacement_text);
$('body').on(data.event, function(e) {
e.preventDefault();
$this.text(original_text);
});
};
Here is the function that sets up the textToggle. At the end of the function, we are triggering another event `clinical.status'.
$('#clinicalPatients').on('click', '[data-role="auth-process"]', function(e) {
e.preventDefault();
var $this = $(this);
var _id = $target.attr('id');
textToggle({
id: _id,
element: $this,
replacement_text: "Processing...",
event: "clinical.status.finished"
});
$('#clinicalPatients').trigger('clinical.status', [{
id: _id,
target: $target,
action: _type
}]);
});
At the end of clinical.status is when I want to fire the event in toggleText, clinical.status.finished. This is the code for that event.
$('body').trigger('clinical.status.finished', [{
id: originalId
}]);
clinical.status.finished is not getting triggered at the right time. There is no other place in the code that is using this, so it has to be the way that I am setting it up. If I leaved that event out of the toggleText function, and drop it in the function where I set up the toggleText function, then everything works like it is supposed to. By putting on event into a separate function, will this cause issues. Please, any help will be appreciated. Thanks.
How can I reference the clicked Ajax.ActionLink from within it's OnBegin function?
cshtml
#Ajax.ActionLink(
typeName,
"OrderQueueRows",
new
{
typeNames = Model.Name,
includeDerivedTypes = ViewBag.IncludeDerivedTypes,
excludeCompletedOrders = ViewBag.ExcludeCompletedOrders
},
new AjaxOptions {
LoadingElementId="ajax-loading",
OnBegin = "highlightFilter",
UpdateTargetId = "order-queue-body"
},
new { #class = "show-exclusively" })
javascript
function highlightFilter() {
$link = $(this);
$link.css('color', 'red');
$link.siblings().not($link).css('color', '');
}
You cannot achieve this with the out-of-the-box Ajax.* helpers because they do not pass the element to the onBeforeSend callback. The way Microsoft implemented it, they pass the xhr object and not the element that was clicked. So one possibility is to modify the jquery.unobtrusive-ajax.js script in order to pass this information to the callback. On line 85 you will see the following inside the beforeSend callback:
result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this, arguments);
All you have to do is to modify it like this:
result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(element, arguments);
and if you needed to have both the element and the xhr object inside your OnBegin callback:
result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply({ xhr: this, element: element }, arguments);
Now, your code is going to work and this will represent the element:
function highlightFilter() {
$link = $(this);
$link.css('color', 'red');
$link.siblings().not($link).css('color', '');
}
Another possibility is to simply replace the Ajax.ActionLink with a standard Html.ActionLink and then write the corresponding jQuery code to subscribe to the .click event and trigger the AJAX request. Personally it's probably what I would do.
This is now fixed in ASP.NET MVC 5.1, using approach from another stackoverflow question: How to use $(this) inside MVC3 Ajax.ActionLink OnBegin,OnComplete Events