how to clone forms in javascript preserving event bindings - javascript

I am about to clone a part of form which has some actions bound to click events. I want to change all occurences of word TEMPLATE in attributes to 'n_'+ID. I want to abstract from structure of cloned part - It would be one TEMPLATE or 10.
I tried to something like this but the bindings are lost:
insert = function(){
var old = $('#TEMPLATE');
var copy = old.clone(true);
var html = old.html().replace(/TEMPLATE/g,'n'+next_ID);
old.html(html);
old.attr('id','n'+next_ID);
old.show('fast');
old.after(copy);
next_ID++;
}
Is there any way to do it easily without knowing anything about the structure of the copied element.

No. You would have to re-add handlers each time.
If you really want to avoid this, use event delegation (delegate() or live()) to attach your event handlers. That way they are not associated with particular node objects, but only the placement of elements, whether they match a selector, at event firing time.
$(myform).delegate('.dosomething', 'click', function() {
// handle clicks on any .dosomething in the form now or added later
});
(And try to avoid text processing over the html()/innerHTML. This is unreliable. It's better to iterate over objects whose names or classes you want to change doing it with attr.)

Related

Parse nodes in a dijit.tree

I'm struggling with a dijit.Tree and I can't find what I need in the dojo documentation...
I want to change the style of a few elements in my tree, according to some conditions.
I am able to identify the elements through a combination of for loops and if evaluations :
itemList = this.tree.model.store._arrayOfAllItems;
for (var index in itemList) {
item = itemList[index];
if (item.<property> == ...) {
...
//This is where I want to change the style
...
}
...
}
But then, I fail to get the node id to call dojo.addClass(nodeId, newClass).
Am I parsing through the proper list, with the model.store._arrayOfAllItems? Is there a way to parse through the node list instead, and still access the data properties?
Thank you very much for your help!
Edit on 2015-11-23
With Richard's comments, I was able to obtain the result I was looking for. I have added a handler that connects the tree's onOpen event to a method that gets the open node map (from tree._itemNodesMap) and then fetch through the store. For every item in the store, it adjust the css if the id of the item being validated has an associated node in the open node map. It then looks recursively for children.
Thanks Richard for your help!
If you have the id of the node inside the tree, you can use the getNodesByItem function that tree has.
Although if your tree is dynamic and the contents can change, I would suggest writing a function that not only adds to your store but also adds to a class for the node formed in the tree.

Looping through selected checkboxes jQuery

I'm trying to get the values of all selected checkboxes with the following code to insert them in a textarea.
$('input[name="user"]:checked').each(function(){
parent.setSelectedGroup($(this).val()+ "\n");
});
but i always get only one value.
How to write the code in a correct way to get the value of ALL selected checkboxes?
Thanks ahead!
EDIT
1) "parent" because the checkboxes are in a fancybox.iframe.
2) setSelectedGroup in the parent window is
function setSelectedGroup(groupText){
$('#users').val(groupText);
You are getting all the values, simply on each loop through the collection you're passing a new value to setSelectedGroup. I assume that method replaces content rather than appending so you are simply not seeing it happen because its too fast.
parent.setSelectedGroup(
//select elements as a jquery matching set
$('[name="user"]:checked')
//get the value of each one and return as an array wrapped in jquery
//the signature of `.map` is callback( (index in the matching set), item)
.map(function(idx, el){ return $(el).val() })
//We're done with jquery, we just want a simple array so remove the jquery wrapper
.toArray()
//so that we can join all the elements in the array around a new line
.join('\n')
);
should do it.
A few other notes:
There's no reason to specify an input selector and a name attribute, usually name attributes are only used with the input/select/textarea series of elements.
I would also avoid writing to the DOM inside of a loop. Besides it being better technique to modify state fewer times, it tends to be worse for performance as the browser will have to do layout calculations on each pass through the loop.
I strongly recommend almost always selecting the parent element for the parts of the page that you're concerned with. And passing it through as the context parameter for jquery selectors. This will help you scope your html changes and not accidentally modify things in other parts of the page.

How to query on object type in dojo?

We can use dojo.query to get certain elements based of CSS selectors but how do we query on object types?
For example, get all the TextBox elements on the page and then use dojo.connect to bind a function?
This is not completely supported, yet there are two ways of doing it as i see it.
One, figure out which is the unique class for a TextBox (.dijitTextBox), call dojo.query('.dijitTextBox'), loop result dojo.forEach and get the widget with dijit.getEnclosingWidget(domnode)
var textboxArray = [];
dojo.forEach(dojo.query('.dijitTextBox'), function(domnode) {
textboxArray.push(dijit.getEnclosingWidget(domnode));
});
Or two, loop the dijit.registry._hash, test declaredClass, if its dijit.form.TextBox - connect.
var textboxArray = dojo.filter(dijit.registry._hash, function(widget) {
return widget.declaredClass && widget.declaredClass == 'dijit.form.TextBox';
})
Depending your setup, choose the most efficient one. The latter is commonly best - unless you have 100's of widgets in your page. The first will have to xpath all your elements of the page. Allthough, remember that dojo.query takes a second parameter as 'parentNode'

With Backbone.js, how to Bind to an array's changes w/o needing to manually trigger?

I'm creating an invitation dialog that allows users to enter emails. Currently in the model I am creating an array to hold the emails:
initialize : function() {
this.model.set({
invite_email_array : new Array()
});
}
And then I'm adding/removing items in the view like so:
this.model.get('invite_email_array').push('email#domain.com');
Then problem is the binder is not being triggered when I either add or remove an email from the model. Here is my binder:
binder : function() {
model.on("change:invite_email_array", onInviteEmailArrayChange() )
}
The only way I was able to get the binding to trigger was to trigger it manually when I make updates.. an ugly hack
this.model.trigger("change:invite_email_array");
Any suggestions on a better way to maintain a list of emails and then be able to bind to the object on add/removes?
Thanks
In the example you give, you're bypassing set by altering the array directly. In order to trigger the change, you would need to set the altered array after pushing the new e-mail. Something to the effect of:
var arr = _.clone(this.model.get('invite_email_array'));
arr.push('email#domain.com');
this.model.set({ invite_email_array: arr });
As soon as you've introduced an array, however, it may be worth considering whether the view's design is really reflecting its intent. Collections (or arrays) of anything often signal that it's time to consider simplifying models or views. Even though a single e-mail seems too trivial assign to its own view/model, it may make sense to track an array of e-mails as a collection of "invitation" views and watch for changes accordingly.

Cant append "used" DOMObject in IE

I have some objects, that keep created DOMObjects, like here:
function category(){
var domBlock;
this.block = function(){
if (!domBlock){
// Here dom-object constructor $('<div></div>'); etc
}
return domBlock; // jquery object,
// but i test and native document.createElement
}
}
Then i clear category's area, and append old and new received(with Ajax) objects:
area.html('');
for(id in category_list){
area.append( category_list[id].block() );
}
where category_list is list that contain category objects. Area is jQuery object.
In other browsers (except IE) i get area with all needed categories, but in IE i get only new received categories(that just create DomObject), other old objects that keeped old DomObject not show.
I know it possible make with innerHTML, but i wont keep DomObject, not text. Because DomObject keep many events. And it very hard for browser attach events for each DomObject after refresh area.
Like comments suggest you can use .clone() for this, to eliminate your other problem, with events not copying, that's covered as well. .clone() takes a boolean parameter, telling it whether to copy data and events (this is as of jQuery 1.4, it was just events, not data, before then).
To use .clone(bool) and get a copy including event handlers, just do this:
return domBlock.clone(true);

Categories