This is a JavaScript/Ajax webpage (also using jQuery).
I have a nested structure I need to display. After displaying a top level element, users can click on it, and see levels below it (dynamically generated).
I don't want to pre-generate everything and hide it with display: none (the page is complex, I'm simplifying for this question) - I want to build the display from the javascript array that was fetched with ajax.
My question:
I have two options:
1: Create a flat array:
[ {id: xx, children: [ xx, xx, .. ] }, ....]
Then for the onclick of an element I get the id from the array, find the children, pull them up from the array and display them. (I guess I'll have to search through the array, since there are no associative arrays in javascript - or make an index.)
2: Create a nested array:
{ id: xx, children [ { id: xx, children : [....] }, {....} ] }
Then somehow bind the children in the array to the element when I display it.
I have two problems with this second approach:
A: I'm constantly copying large chunks of the array for each child when I create it. (At least I think I am. Do I need to use deep copy? Can I make a reference?)
B: I'm not sure how to bind the data to the child element. Normally I build the display using html strings with onClicks, then append the entire thing. But onClicks can only take an ID, not a copy of an array.
I did something similar recently where I had a very large nested structure (over 2000 nodes) - which I did not want to bulk append to the DOM.
What I ended up doing was taking the ajax loaded data and converting it into a nested structure...
<node id="1" title="a">
<node id="2" title="b />
<node id="3" title="b">
<node id="4" title="d" />
</node>
</node> etc...
...and storing this as a jQuery object (nodes), but never appending it to the DOM.
I could then select the immediate children of a node as I needed them relatively easily, for converting into html elements and appending to the DOM, adding data, etc...
$("#"+ID+">node", nodes).each(function() {
var node = $(this);
//do whatever...
});
I don't know if this is the most memory-efficient approach, but it certainly makes it very easy to select and append the immediate children of a node to the DOM as you need them.
I would prefer to use the second approach, for the reason it has a better structure as well as you can write less code as recursive comes into play.
You say that your not sure how to bind the child elements to the array without actually creating dom elements, well if you use <!DOCTYPE html> for html you elements can have html-* attributes allowing you to store data in an element, example:
<ul id="lists">
<li class="parent" id="root_22" data-children="{some object}">A Root Elelment</li>
</ul>
the problem with this method is that you would have to store every children of children in the root element, which more than likely is a overhead.
Another way is to bind the data using jQuery.data method, this will keep the DOM clean but will atatch data to an element.
Store arbitrary data associated with the specified element.
#see: http://api.jquery.com/jQuery.data/
Related
I have an approach to attach functionality to HTML code with custom data attributes like
<div data-prefix-f1="..." data-prefix-f2="..."></div>
Now I'd like to list all Elements having dataset, or better those having data-prefix-* attribute. My current approach is
<div data-prefix data-prefix-f1="..." data-prefix-f2="..."></div>
<script>
container.querySelectorAll('[data-prefix]').forEach(el => /* examine el.dataset */)
</script>
But I'd like to have the HTML source as lean as possible and the empty data-prefix is kind of redundant and the code less readable. On the other hand, looping and examining ALL elements would be too expensive overhead. Any idea to effectively iterate Elements having data-prefix-* attributes?
I'm looking to display a very large set of data in a tree model. Nodes may have multiple children, as well as multiple parents. Ideally the tree should be dynamic as it represents dynamic data.
I have taken a look at a few different JS libraries such as D3, Raphael, etc. and while they fit the bill, I am not able use any JS libraries other than JQuery. I am looking for a solution using only HTML, CSS, and JS/JQuery.
The sole audience is IE8.
Any suggestions?
Html itself is a representation of a tree structure. Each element is a node and elements inside a node are children well the node itself is a parent.
If displaying data is all that is needed you could do it like:
<div> <-- Parent
<p>Data Node Parent</p> <-- Data
<div> <-- Child
<p>Data Node Child</p> <-- Child Data
</div>
</div>
Then use some css to style it like a like a tree.
Using jquery you can traverse the dom (tree) and add the data to each of these elements and or create the elements dynamically based on the data. You would be using methods like .next() .prev() .parent() .child()
http://api.jquery.com/category/traversing/
http://api.jquery.com/category/manipulation/
Actual case is much more complicated but please play along. I am trying to select siblings of element that has class 'sss', by using
$('.sss').parent().parent().find(">div.childCollapsible>div[data-onthemovecollapsible=true]")
I can only use CSS selectors (this is part of Selenium thest). I expected to get only siblings of 'sss' however I am getting all the children of sub elements too.
How could I restrict it only to siblings?
or any other workaround that can get me from any element in the tree siblings only of any
data-onthemovecollapsible="true"
attribute holder.
EDIT: Firstly I would like apologise for failing to express myself clearly. The structure that I am working with is 'infinite tree structure' that has unknown amount of nodes on each layer, mechanism I am looking for is ability to get siblings on the same level that I am starting search from is and only children of his parent (his brothers + himself). All levels of tree have identical HTML syntax, so looking at them relatively from element one starts from, each layer is identical, hence the CSS selector should be identical too. I cannot use any other Jquery method but 'find', and only can use CSS selectors, as mechanism is part of selenium test so only By.CssSelector("...") can be used. I can traverse up the elements by using element.FindElements(By.XPath("..")) that gets me parent as I know how many levels up parent is, but from parent position I need to get all siblings without children (that have identical html syntax) in one go, so i would assume selector with only certain layer should do (like one in jsfiddle below), however it selects all the children nodes too - does not respect '>' for some reason. This would do nicely if I could use all JQuery functions.
$('.sss').parent().parent().children().children()
what I need is same result but with CSS selector.
http://jsfiddle.net/2a46U/
I think this will work for you:
.find("body>div>div>div>div.childCollapsible>div[data-onthemovecollapsible=true]")
If I'm understanding this correctly, you have two different restrictions here. One is that you only want siblings of an .sss element. The other is that the parent of the element is div.childCollapsible. I don't believe you will be able to do this with a single selector/find. You would need something like this:
// get the siblings of .sss with appropriate data attribute
var $els = $('.sss').siblings("div[data-onthemovecollapsible=true]");
// filter the collection to only those with appropriate parent
$els = $els.filter(function(){
return $(this).parent().is("div.childCollapsible");
});
http://jsfiddle.net/2a46U/4/
I've updated your jsfiddle with two options (check the console please):
Get all the siblings:
$('.sss').siblings();
Get specific siblings:
$('.sss').siblings("div.AppletBase")
If you need to set styles you can use the siblings selector in CSS3:
.sss ~ div.AppletBase {/* Your styles in here */}
Anything please leave a comment and I will review it again if is needed
I was writing some code in jQuery, like:
$(".middleContent").not(this).slideUp();
(this = currently active .middleContent element)
And I was wondering how JavaScript knew the elements index in the DOM.
I know each object is unique, but if you have a few elements with the same class how does it distinguish between them? It is to do which its index in the tree of all the elements, like how a program has an address in RAM?
Each dom element is an individual object and unique. The not does comparisons on the current execution context ( this ) to make sure that any element inside the array doesnt equal this.
I think you're underestimating what it means for a DOM element to be unique. It's not only the class, tag name or index within the current parent element that identifies a DOM element. Each DOM element internally has a unique identifier, which is not accessible to you. It's used by the browser to organize the DOM internally. There can be hundreds of seemingly identical <div class="middleContent" /> elements in your page, each single one has a unique internal identifier. If you compare one DOM element to another, the browser will always be able to tell whether it's the same element or one that just looks like it.
this refers to one specific DOM element, therefore jQuery is able to filter it out of a collection of seemingly similar elements.
The elements in the DOM are just objects themselves, organised into a tree structure, so they have next and previous siblings at the same level, their own list of children, a parent. From this you can walk around the structure of the tree and manipulate it.
You can obtain the object(s) inside a jQuery object by using indexing notation:
var caption = $('#caption');
var domElement = caption[0];
Then domElement will contain one of these.
Given the following markup.
<div id="example">
<div>
<div>
<input type='hidden'></input>
</div>
</div>
</div>
How can I quickly get the hidden input element given I have the ID for the top most div element with the ID 'example'?
I can hack away at it so I can just iterate through each child element until I hit the input, however, I'd like to improve on that and utilize Prototype and simply jump to that hidden input given the div.
Thanks!
Prototype provides a whole bunch of ways to do this:
// This, from Bill's answer, is probably the fastest, since it uses the
// Browser's optimized selector engine to get straight to the element
$$('#example input[type=hidden]').first();
// This isn't bad either. You still use the browser's selector engine
// To get straight to the #example element, then you must traverse a
// (small) DOM tree.
//
// element.down(selector) selects the first node matching the selector which
// is an decendent of element
$('example').down('input');
// Here, you'll get an array containing all the inputs under 'example'. In your HTML
// there is only one.
$('example').select('input')
// You can also use element.select() to combine separate groups of elements,
// For instance, if you needed all the form elements:
$('example').select('input', 'textarea', 'select');
$$('#example input[type=hidden]').first()
I prefer the direct approach
document.forms[0].fieldName.value
Which is less code, no need to use jQuery and is more friendly to code design.