I have links on a table to edit or delete elements, that elements can be filtered. I filtered and get the result using ajax and get functions. After that I added (display) the result on the table using inner.html, the issue here is that after filtering the links on the elements not work, cause a have the dojo function like this
dojo.ready(function(){
dojo.query(".delete-link").onclick(function(el){
var rowToDelete = dojo.attr(this,"name");
if(confirm("Really delete?")){
.......
}
});
I need to trigger the event after filtering, any idea?
(I'm assuming that you're using Dojo <= 1.5 here.)
The quick answer is that you need to extract the code in your dojo.ready into a separate function, and call that function at the end of your Ajax call's load() callback. For example, make a function like this:
var attachDeleteEvents = function()
dojo.query(".delete-link").onclick(function(el){
var rowToDelete = dojo.attr(this,"name");
if(confirm("Really delete?")){
.......
}
});
};
Then you call this function both in dojo.ready and when your Ajax call completes:
dojo.ready(function() { attachDeleteEvents(); });
....
var filter = function(someFilter) {
dojo.xhrGet({
url: "some/url.html?filter=someFilter",
handleAs: "text",
load: function(newRows) {
getTableBody().innerHTML = newRows;
attachDeleteEvents();
}
});
};
That was the quick answer. Another thing that you may want to look into is event delegation. What happens in the code above is that every row gets an onclick event handler. You could just as well have a single event handler on the table itself. That would mean there would be no need to reattach event handlers to the new rows when you filter the table.
In recent versions of Dojo, you could get some help from dojo/on - something along the lines of:
require(["dojo/on"], function(on) {
on(document.getElementById("theTableBody"), "a:click", function(evt) {...});
This would be a single event handler on the whole table body, but your event listener would only be called for clicks on the <a> element.
Because (I'm assuming) you're using 1.5 or below, you'll have to do it a bit differently. We'll still only get one event listener for the whole table body, but we have to make sure we only act on clicks on the <a> (or a child element) ourselves.
dojo.connect(tableBody, "click", function(evt) {
var a = null, name = null;
// Bubble up the DOM to find the actual link element (which
// has the data attribute), because the evt.target may be a
// child element (e.g. the span). We also guard against
// bubbling beyond the table body itself.
for(a = evt.target;
a != tableBody && a.nodeName !== "A";
a = a.parentNode);
name = dojo.attr(a, "data-yourapp-name");
if(name && confirm("Really delete " + name + "?")) {
alert("Will delete " + name);
}
});
Example: http://fiddle.jshell.net/qCZhs/1/
Related
I have an element with an on('mouseenter') event handler attached. The problem is the event does not fire on the selected element if the response time that transition to a sub node element of the element that the event is really attached to.
Querying: element.tagName gives the correct value sometimes and wrong other times causing event firing issues.
Solution: the solution was to add an array with valid tag names then query the parent of the element the event fired on until a valid tag name is found.
EXTJS is well annoying.
var elements = Ext.select('tag1, tag2, tag3', true);
elements.on('mouseleave', function(e, el){
var tags = ['TAG1', 'TAG2', 'TAG3']; // valid event Tags in caps;
var els = el;
if(tags.includes(els)){
while(true){
els=Ext.get(els).parent("",true);
if(tags.includes(els.tagName)){break;}
}
}
// now els contains the correct tag
}, this);
// duplicate function and change event for on('mouseleave');
elements.on('mouseleave', function(e, el){
var tags = ['TAG1', 'TAG2', 'TAG3']; // valid event Tags in caps;
var els = el;
if(tags.includes(els)){
while(true){
els=Ext.get(els).parent("",true);
if(tags.includes(els.tagName)){break;}
}
}
// now els contains the correct tag
}, this);
// duplicate function and change event for on('mouseleave');
I need to replace old event listener code with a new, but without deleting old:
onchange="func1();func2();"
need to be replace by:
onchange="newFunc();func1();func2();"
Adding new code to element's onChange event via JQuery was simple:
var element = $('element');
var _onchange = element.onchange;
element.onchange = function() {
//some additional code
if (typeof(_onchange) == 'function') {
_onchange();
}
};
Now I need to rewrite it using Prototype.. I suppose it will be like:
element.observe('change', function() {
// new code here
// then old onChange
}.bindAsEventListener(element)));
How can I grab existing onChange's code?..
You actually don't need to .bindAsEventListener() except in rare cases
so adding the event observing to an element
$('element').observe('change',function(){
//onchange code
});
If you do this - it will add the new code as an observer without deleting any other observers setup for that element and event
Here is the problem:
By default jQuery Mobile is using GET requests for all links in the application, so I got this small script to remove it from each link.
$('a').each(function () {
$(this).attr("data-ajax", "false");
});
But I have a pager in which I actually want to use AJAX. The pager link uses HttpPost request for a controller action. So I commented the above jQuery code so that I can actually use AJAX.
The problem is that when I click on the link there are two requests sent out, one is HttpGet - which is the jQuery Mobile AJAX default (which I don't want), and the second one is the HttpPost that I actually want to work. When I have the above jQuery code working, AJAX is turned off completely and it just goes to the URL and reloads the window.
I am using asp.net MVC 3. Thank you
Instead of disabling AJAX-linking, you can hijack clicks on the links and decide whether or not to use $.post():
$(document).delegate('a', 'click', function (event) {
//prevent the default click behavior from occuring
event.preventDefault();
//cache this link and it's href attribute
var $this = $(this),
href = $this.attr('href');
//check to see if this link has the `ajax-post` class
if ($this.hasClass('ajax-post')) {
//split the href attribute by the question mark to get just the query string, then iterate over all the key => value pairs and add them to an object to be added to the `$.post` request
var data = {};
if (href.indexOf('?') > -1) {
var tmp = href.split('?')[1].split('&'),
itmp = [];
for (var i = 0, len = tmp.length; i < len; i++) {
itmp = tmp[i].split('=');
data.[itmp[0]] = itmp[1];
}
}
//send POST request and show loading message
$.mobile.showPageLoadingMsg();
$.post(href, data, function (serverResponse) {
//append the server response to the `body` element (assuming your server-side script is outputting the proper HTML to append to the `body` element)
$('body').append(serverResponse);
//now change to the newly added page and remove the loading message
$.mobile.changePage($('#page-id'));
$.mobile.hidePageLoadingMsg();
});
} else {
$.mobile.changePage(href);
}
});
The above code expects you to add the ajax-post class to any link you want to use the $.post() method.
On a general note, event.preventDefault() is useful to stop any other handling of an event so you can do what you want with the event. If you use event.preventDefault() you must declare event as an argument for the function it's in.
Also .each() isn't necessary in your code:
$('a').attr("data-ajax", "false");
will work just fine.
You can also turn off AJAX-linking globally by binding to the mobileinit event like this:
$(document).bind("mobileinit", function(){
$.mobile.ajaxEnabled = false;
});
Source: http://jquerymobile.com/demos/1.0/docs/api/globalconfig.html
So I have been adding my events thusly:
element.addEvent('click', function() {
alert('foobar');
});
However, when attempting to remove said event, this syntactically identical code (with "add" switched to "remove") does not work.
element.removeEvent('click', function() {
alert('foobar');
});
I assume this is because the two functions defined are not referenced the same, so the event is not technically removed. Alright, so I redefine the event addition and removal:
element.addEvent('click', alert('foobar'));
element.removeEvent('click', alert('foobar'));
Which works great, except now when the page loads, the click event is fired even before it's clicked!
The function is removed, though, which is great......
update: when you do .addEvent('type', function(){ }) and .removeEvent('type', function(){ }), even though the functions may have the same 'signatures', they are two separte anonymous functions, assigned on the fly. function 1 is !== to function 2 - hence there is no match when MooTools tries to remove it.
to be able to remove an exact handler, o:
function handler(){ ... }
el.addEvent('click', handler);
// .. later
el.removeEvent('click', handler);
Internally, events are actually a map of keys to functions in element storage. have a look at this fiddle i did a while back for another SO question - http://www.jsfiddle.net/mVJDr/
it will check to see how many events are stacked up for a particular event type on any given element (or all events).
similarly, removeEvent looks for a match in the events storage - have a look on http://jsfiddle.net/dimitar/wLuY3/1/. hence, using named functions like Nikolaus suggested allows you to remove them easily as it provides a match.
also, you can remove events via element.removeEvents("click") for all click events.
your page now alerts because you pass on alert as the function as well as execute it with the params 'foobar'. METHOD followed by () in javascript means RUN THE METHOD PRECEDING IT IMMEDIATELY, NOT LATER. when you bind functions to events, you pass the reference (the method name) only.
to avoid using an anonymous function and to pass argument,s you can do something like:
document.id('foobar').addEvent('click', alert.bind(this, 'foo'));
as bind raps it for you, but removing this will be even more complicated.
as for event delegation, it's:
parentEl.addEvents({
"click:relay(a.linkout)": function(e, el) {
},
"mouseover:relay(li.menu)": function(e, el) {
}
});
more on that here http://mootools.net/docs/more/Element/Element.Delegation#Element:removeEvent
keep in mind it's not great / very stable. works fine for click stuff, mouseenter is not to be used delegated, just mouseover - which means IE can fire mouseout when it should not. the way i understand it, it's coming improved in mootools 2.0
edit updating to show an example of bound and unbound method within a class pattern in mootools
http://www.jsfiddle.net/wmhgw/
var foo = new Class({
message: "hi",
toElement: function() {
return this.element = new Element("a", {
href: "http://www.google.com",
text: "google",
events: {
"click": this.bar.bind(this), // bind it
"mouseenter": this.bar // unbound -> this.element becomes this
}
});
},
bar: function(event) {
event.stop();
// hi when bound to class instance (this.message will exist)
// 'undefined' otherwise.
console.log(this.message || "undefined");
}
});
document.id(new foo()).inject(document.body);
the mouseenter here will be unbound where this will refer to the default scope (i.e the element that triggered the event - the a href). when bound, you can get the element via event.target instead - the event object is always passed on to the function as a parameter.
btw, this is a slightly less familiar use of class and element relation but it serves my purposes here to illustrate binding in the context of classes.
assig the function to a variable and use the same reference to add and remove the event.
if you use an anonymous function you will get to different references
var test = function(){ alert('test: ' + this.id); }
$('element').addEvent('click', test);
...
$('element').removeEvent('click', test);
addEvent : Attaches an event listener to a DOM element.
Example -
$('myElement').addEvent('click', function(){
alert('clicked!');
});
removeEvent : Works as Element.addEvent, but instead removes the specified event listener.
Example -
var destroy = function(){ alert('Boom: ' + this.id); } // this refers to the Element.
$('myElement').addEvent('click', destroy);
//later...
$('myElement').removeEvent('click', destroy);
This means when you add an event with a eventhandler not an anonymous function if you than remove the event than it will be removed.
I'm getting an error when parsing checkboxes in a table that is loaded with AJAX, but I get an error saying the widget with that id is already registered:
"Error('Tried to register widget with id==userListUncheckAll but that id is already registered')"
And I'm guessing this happens because we take out the current table, then replace it with what ever we get back from the AJAX call, and thus the element id's would be the same. Is there a way to "unregister" the widgets or something similar?
I found the answer for this myself, so I'll put it here for others:
If you have a set of id's that you know will need to be "unregistered", create an array of the id names:
try {
dojo.parser.parse();
} catch (e) {
var ids = ['id1', 'id2', 'id3'];
dijit.registry.forEach(function(widget) {
//remove this check if you want to unregister all widgets
if (dojo.indexOf(ids, id) {
widget.destroyRecursive();
}
});
dojo.parser.parse();
}
Works like a charm.
Get the parent node that you are inserting the AJAX content into and parse ONLY this node. You are getting this error because other widgets in your DOM are getting parsed twice. Something like this:
require(["dojo/dom", "dojo/parser", "dojo/_base/xhr"/*, etc */ ],
function(dom, parser, xhr) {
var request = xhr.get({
// your details here
});
request.then(function(data) {
// transform data if necessary
var parentNode = dom.byId("/* parent id */");
parentNode.innerHTML = data;
// This is where the widgets get built!
parser.parse(parentNode); // or parser.parse("/* parent id */");
}, function(err) {
// handle error
});
});
Also, make sure you include the right dojo / dijit modules. A common mistake is to forget to include the modules for the widgets that you are trying to insert. For example, if you are using TabContainer, add "dijit/layout/TabContainer" to the list of required modules.
Within the Dojo Parser documentation is included this code snipet:
dojo.xhrGet({
url: "widgets.html",
load: function(data){
dojo.byId("container").innerHTML = data;
dojo.parser.parse("container");
}
})
I applied in my code and works fine.