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);
Related
I have an undirected graph in Vis.js and I would like to change the color and size of the adjacent nodes (scaling them according to values in a JS array) when a certain node is selected. How would I go about doing this? The documentation for vis.js network objects is unenlightening beyond the source for this example.
You can listen for click events to know when a user clicked a node.
network.on("click", function (params) {
console.log(params);
});
If you have creates your nodes in a DataSet, you can simply update them and Network will be automatically updated accordingly:
nodes.update({id: 4, label: "changed label"});
Elaborating on this answer in response to this question. The vis.js->Network documentation has all the details, you just have to put them in order.
You use the "on" method of the network instance in order to listen for events. See "Method Reference -> Global" at the link above. This "on" method takes two inputs. The first is the event to be listened for; the second is a function that specifies the action to be taken when the event occurs.
To understand how to use this information, see the "Events" section in the documentation link above. For click events, your code will look something like
network.on("click", function (params) {
console.log(params);
});
The first argument is always a string; in this case we are interested in the "click" event. The second argument is a callback function that takes a single argument (I called this argument "params" in the example above). The "Events" documentation (again, see link above) summarizes this structure for you. Specifically, if the click event is associated with a node, then the ID of the node that was clicked is accessible as params.nodes[0].
Back to the original question. To change the color of adjacent nodes, you first need an array of the adjacent nodes. You do this using the "getConnectedNodes" method (see "Method Reference -> Information" at the link above). This will give you an array of node IDs. Next, for each ID in that array, you need to update the properties you wish to change.
The easiest way to update node properties is to create your nodes using a DataSet. You are probably already doing so. See this example, and notice the lines
var nodes = new vis.DataSet([...]);
This nodes variable has its own update method. So if you have (e.g.,) a variable CurrentID that holds the node ID of a node you wish to modify, and you want to (e.g.,) change the label of that node to the text string stored in another variable newLabel, you would do
nodes.update({id:CurrentID, label:newLabel});
Thanks much for these helpful responses; one of the gotchas is that the assertion that one is probably already creating one's own dataset is likely false if using the Network documentation examples, which mostly do something like the following:
var nodeArr = [...];
var edgeArr = [...];
data = {nodeArr, edgeArr}
network = new vis.Network(container, data, options);
The update function is only available if the nodes are a Dateset rather than an array:
var nodeArr = [...];
var edgeArr = [...];
data = {new vis.DataSet(nodeArr), new vis.DataSet(edgeArr)}
network = new vis.Network(container, data, options);
Suppose I have a div tag like this:
<div id="group-dialog" class="modal-dialog">
Now I want to grab it as a jQuery object (in this case so I can run .dialog()).
When I try this:
var gDialog = $('#group-dialog');
I get an array back (!!).
Why am I getting an array? Isn't the point of having an ID attribute that there's only one? I can see getting multiple p's or .my-css-thing back ...
Next question:
I have this array with 1 object in it that I now want to access as a jQuery object.
When I do this:
$(gDialog[0])
And pull it up in F12, I still have an array!! I thought I de-referenced the array already by picking the first element.
This doesn't seem to help either:
var gDialog = $('#group-dialog:first');
This is basic, but I run into this problem a lot. It seems like it used to be a lot simpler!
What is the best way to access this DOM element as a jQuery object?
Answer 1
jQuery selectors always return arrays.
Selection with id attribute is a particular use case and ideally the result should be unique. However, there is nothing preventing you from having duplicated ids in a HTML document (although this is bad practice).
Answer 2
The following code will get you the first element as a DOM object:
var gDialog = $('#group-dialog')[0];
Note: you may want to check the size of the return array first.
As far as I know, there is no way to transform this DOM element back to a jQuery object. The standard use case would be to directly used $('#group-dialog') and asume that it is found and unique.
Try using .get(). Though I'm not sure it will work with dialog()
Retrieve the DOM elements matched by the jQuery object.
var gDialog = $('#group-dialog').get();
If you're trying to grab it to use it on a dialog, you can just put
$(document).ready(function(){
$('#group-dialog').dialog({put options here})
});
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.
Is there any problem with loading the same set of values in 2 different HTML form dropdowns? My code looks like this:
var dr1=document.getElementById("dr1");
var dr2=document.getElementById("dr2");
for (nombre in elements) {
var opcion=document.createElement('OPTION');
var cam=elements[nombre];
opcion.value=nombre;
opcion.text=cam["nombreCompleto"];
//AƱadimos a los 2 dropdowns
dr2.add(opcion, null);
dr1.add(opcion, null);
}
dr1.selectedIndex=0;
dr2.selectedIndex=0;
This load the same set of values to two different dropdowns. However, when executed, it only loads whatever dropdown appears last in the code; in the above example, it would have been "dr1" (and if I put the "dr2.add(option.null)" line last, it loads that one). If I load only one dropdown (commenting out the other one) it works fine.
All of this is on Firefox 3.6.10.
Yes, your OPTION objects will first be added to dr2, then to dr1. There won't be created copies when calling add, but the object you just created will be moved from nowhere to dr2, then to dr1.
The general idea is that you can't have a DOM object in two different places at the same time. You may want to take a look into JavaScript object cloning. See here for some useful information: What is the most efficient way to deep clone an object in JavaScript? .
If you just need to clone DOM element objects you can use cloneNode(). See here for a complete list of available members and methods: http://www.w3schools.com/jsref/dom_obj_all.asp
No, it doesn't work. Refactor the code to create the option node into a function.
function createOption(...) {
var opcion=document.createElement('OPTION');
var cam=elements[nombre];
opcion.value=nombre;
opcion.text=cam["nombreCompleto"];
return opcion;
}
dr1.add(createOption(), null);
dr2.add(createOption(), null);
To add it to the second element just clone it ..
dr2.add(opcion, null);
dr1.add(opcion.cloneNode(true), null);
example at http://www.jsfiddle.net/7Kxdu/
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.)