I'm new to JSTree, but really want to know how do you traverse jstree to edit each node?
I wrote a recursive function to traverse each nodes, but strange thing to me is,
I take out the data using var treeData =
$('tree').get_json(true);
I put this data into the function , traverse(treeData);
traverse(treeData){ //do some editing work here.
traverse(treeData'schildren);
}
Wired, after the traverse finished, nothing is changed.
I've already set the 'check_callback' to true.
Anyone can help?
BEST regards
Michael
With get_json you only get json object. To change nodes you have to access them. Also you have to use redraw function to update nodes based on changed data.
An example of your functionality could look like:
function traverse(node) {
node.text += ' too';
for (var i=0, len = node.children.length; i<len; i++) {
var childNode = $("#tree").jstree().get_node(node.children[i]);
traverse(childNode);
}
}
var treeData = data.node;
traverse(data.node);
$('#tree').jstree().redraw(true);
Check fiddle - Fiddle
Related
I have a JSON response from a server, which returns me a array with 32 objects (in this case). Something like this:
[{object1},{ object2},{ object3}, etc].
Each object have some info that I use to populate an html template. For that, I just use a simple loop:
for(var i = 0; i < api_empresaListar.length; i++)
{
var item = api_empresaListar[i];
var htmls;
htmls = $('...lots of html code');
...
Then it’s just a simple matter of finding/changing the values, and append items on the DOM. Everything works fine. BUT, for some next parts of the code, I would like to access all the info from the object I used to build the html elements (I just show part of the info). So, after searching a lot, I tried to use data, like this:
var tp = htmls.find(".rl_grupo"); // the main div of each html element created in the loop
$(tp).data('key', api_empresaListar[i]); // here, I expected to just insert the object data in each created item.
But when I try it in the console, I got the object info as expected, but always from the last element in the array. Why is that happening? I believe it might be something stupid, but I can’t figure it out.
So, any ideas on how to solve this, or another method to make this work is appreciated. I made it work by setting some "display:none" placeholder html tags and populate those with the info I need later, but looks like a poor solution...
You should not set your htmls variable in the loop. I think that you crush its content every turn, that's why you only have the last item. You should do something like this:
var htmls = $('<div></div>');
for(var i = 0; i < api_empresaListar.length; i++) {
htmls.append($('...lots of html code'));
}
How about setting an index number on each element inside of your html creating code, then iterating over the $('.rl_grupo') elements, like this?
$('.rl_grupo').each(function(){
var index = $(this).data('index');
var currentData = api_empresaListar[index];
$(this).data('key', currentData);
})
Just to clarify what I'm trying to do, I'm trying to make a Chrome extension that can loop through the HTML of the current page and remove html tags containing certain text. But I'm having trouble looping through every html tag.
I've done a bunch of searching for the answer and pretty much every answer says to use:
var items = document.getElementsByTagName("*");
for (var i = 0; i < items.length; i++) {
//do stuff
}
However, I've noticed that if I rebuild the HTML from the page using the elements in "items," I get something different than the page's actual HTML.
For example, the code below returns false:
var html = "";
var elems = document.getElementsByTagName("*");
for (var i = 0; i < elems.length; i++) {
html += elems[i].outerHTML;
}
alert(document.body.outerHTML == html)
I also noticed that the code above wasn't giving ALL the html tags, it grouped them into one tag, for example:
var html = "";
var elems = document.getElementsByTagName("*");
alert(elems[0].outerHTML);
I tried fixing the above by recurssively looking for an element's children, but I couldn't seem to get that to work.
Ideally, I would like to be able to get every individual tag, rather than ones wrapped in other tags. I'm kind of new to Javascript so any advice/explanations or example code (In pure javascript if possible) as to what I'm doing wrong would be really helpful. I also realize my approach might be completely wrong, so any better ideas are welcome.
What you need is the famous Douglas Crockford's WalkTheDOM:
function walkTheDOM(node, func)
{
func(node);
node = node.firstChild;
while (node)
{
walkTheDOM(node, func);
node = node.nextSibling;
}
}
For each node the func will be executed. You can filter, transform or whatever by injecting the proper function.
To remove nodes containing a specific text you would do:
function removeAll(node)
{
// protect against "node === undefined"
if (node && node.nodeType === 3) // TEXT_NODE
{
if (node.textContent.indexOf(filter) !== -1) // contains offending text
{
node.parentNode.removeChild(node);
}
}
}
You can use it like this:
filter = "the offending text";
walkTheDOM(document.getElementsByTagName("BODY")[0], removeAll);
If you want to parametize by offending text you can do that, too, by transforming removeAll into a closure that is instantiated.
References to DOM elements in JavaScript are references to memory addresses of the actual nodes, so you can do something like this (see the working jsfiddle):
Array.prototype.slice.call(document.getElementsByTagName('*')).forEach(function(node) {
if(node.innerHTML === 'Hello') {
node.parentNode.removeChild(node);
}
});
Obviously node.innerHTML === 'Hello' is just an example, so you'll probably want to figure out how you want to match the text content (perhaps with a RegEx?)
I have the following piece of code to generate an select list on the fly. The code works okay when the records retrieved are between 0-300 records. However when I try to work with records over this number, 671 records to be exact, the browsers(IE,FF,Chrome,Opera) hang and it seems the javascript is taking up a lot of resources and the browsers become slow after the select option is finally generated after a 5 minute wait....
I am looking for a way to make this more efficient and prevent such hangups as I will work with records upto 5000
$("#idea_selectrel").on("change",function(){
var attributeid = $("option:selected",this).val();
$.post("/OCSRM/PopulateHTMLTablesServlet.do",{"attributeid":attributeid,"table":'idearelations'},function(data){
if(!$.isEmptyObject(data))
{
$("#idea_selectrelvalue option:not(:first)").remove();
var html='';
var len = data.length;
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
$("#idea_selectrelvalue").append(html).show();
}
}
else
{
alert('No data found.');
}
},'json');
//alert(attributeid);
});
Your code is building up a long string of HTML containing the options. It's also appending the string over and over again to the <select> element.
Move the .append() to after the loop:
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
}
$("#idea_selectrelvalue").append(html).show();
Not being able to test the code but from what I can tell you're not clearing the html variable inside the loop, currently, the html will be added recursively for every iteration, and every iteration is updating the DOM, which will get pretty large, fast.
I'm not sure if this is by design, but if it's not, try moving the append outside of the loop.
var html='';
var len = data.length;
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
}
$("#idea_selectrelvalue").append(html).show();
That is a lot of DOM elements that will be slow to load even if created without JS. Assuming you don't have a way to pre-generate the select on the server and send it down, you could try not appending the value in the loop. You could build out a string of option values and then, after the string is created, set the html of the select to the generated string. The DOM manipulation in the loop could be a culprit at that level. However, it is important to note that large or deeply nested DOM elements can be a performance concern regardless.
Anyone ever need to hash by object reference in javascript?
Maybe you want to group the children of uls by a hash of their DOM nodes, or attributes by instances of constructed objects, or many other things.
I have read this and don't want to use Weakmaps until they're stable.
The only way I can think to do it is storing parents in an array and enforcing reference uniqueness there, then hashing by the index of the reference in the array. (example is finding all the common UL parents of LIs):
var lis = document.getElementsByTagName('li'); // Get all objects
var parent, key, li;
for (var i=0; i<lis.length; i++ ) { // For every LI
storeItemByParent(lis[i], lis[i].parentNode);
}
var parents = [];
var itemsByParent = {};
function storeItemByParent (item, parent){
var key;
// Does this parent already exist? if so, use that index as the hash key
for (var j=0; j<parents.length; j++) {
if(parents[j] === parent) {
key = j;
break;
}
}
// This is a new parent? If so, track it and use that index as the hash key
if (key == undefined) {
parents.push(parent);
key = parents.length;
}
// Finally! The lis in a hash by parent "id".
itemsByParent[key] = itemsByParent[key] || [];
itemsByParent[key].push(item);
}
Is there a better way to store attributes or children or other things attached to an object instance without adding them as properties of the object itself?
You've pretty much got the right approach there, though I'd wrap it up nicely behind an interface, complete with my own add and find methods.
You can simply your coding somewhat by using <array>.indexOf, supported in all modern browsers and easily polyfilled when needed.
There is a downside to your approach however:
Should an element by removed from the DOM, that element won't be garbage collected because your array is still holding onto a reference to it.
While this isn't a show stopper, of course, it is worth keeping in mind.
There is, however, a totally different approach that you can take as well. May not be better -- but different.
Please forgive any minor errors in the code, I am typing this freehand:
function elementDataHash = {};
function setElementData(el, data) {
var hash = el.getAttribute("data-element-hash");
if !(hash) {
// http://stackoverflow.com/questions/6860853/generate-random-string-for-div-id
hash = generateRandomString();
el.setAttribute("data-element-hash", hash);
}
elementDataHash[hash] = data;
}
function getElementData(el) {
var hash = el.getAttribute("data-element-hash");
return elementDataHash[hash];
}
Why doesn't this work?
var row = document.getElementById(currentRow);
var otherRow = document.getElementById(targetRow);
row.cells[0] = otherRow.cells[0];
This works with
row.cells[0].innerHTML = otherRow.cells[0].innerHTML;
However, there are attributes attached to the cell which I also want to move over without having to manually recreate them.
Solution (Note: more is being done in my actual implementation, but this is the framework):
for (var i = 0; i < 4; i++) {
var copyTo = row.cells[i];
var copyFrom = otherRow.cells[i].cloneNode(true);
copyTo.parentNode.replaceChild(copyFrom, copyTo);
}
You should be able to use cloneNode() to actually clone the node and its attributes.
Each entry in cells refers to a DOMElement. When you typed row.cells[0] = otherRow.cells[0], you are saying that you want row.cell[0] to reference the same DOMElement as otherRow.cells[0].
I'm guessing you want row.cells[0] to have the same text or HTML as otherRow.cells[0]; in which case, the second code snippet will do just that, since you are actually modifying the DOMElement, and not just changing which DOMElement you are referencing.