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/
Related
I have 2 lit-element components. The parent content is :
<s-sortablelist>
${this.renderChildren()}
</s-sortablelist>
The renderChildren function is looping over an array property children = [1, 2] and creating <div> elements. The rendered page might be something like :
<s-sortablelist>
<div id="1"></div>
<div id="2"></div>
</s-sortablelist>
The sortablelist component allows the user to reorder the <div> tags using drag and drop. After dnd, the rendered layout might become (2 and 1 are reverted) :
<s-sortablelist>
<div id="2"></div>
<div id="1"></div>
</s-sortablelist>
After the user changed the order, if I change the property children=[3,4], the result is different from my expectations :
I'm expecting to see a new list with the children 3,4
I'm seeing a list with 3,4, and some other elements (1, 2) depending on the dnd operations I made before
So my question is how is it supposed to work ?
If the children array changes, because it is a property, the parent component will render.
I'm expecting also the sotablelist component to be rerendered, but why would I have extra children from a previous render?
You can't mutate the DOM under control of lit-html this much. lit-html places comment nodes into the DOM to keep track of where the dynamic template parts are, and by moving elements around you're breaking the bookkeeping.
The right way to do this is to not move nodes in the drag and drop operation, but right before you would have actually changed the DOM, instead change the data that rendered the DOM. Then lit-html can render the list on the new order, but keep all the comment node and other internal data in sync.
In order to to get Polymer's data-binding without creating a custom element, I am using the "dom-bind" template helper. Later on, I am going to need to access the nodes inside the template so I can use masonry.js
to create a grid out of the data.
Here is the my template that is inside the main document:
<!-- Skills -->
<template is="dom-bind" class="careerSkills_consumer projects_consumer" id="resume-container">
<page-section id="resume">
<section-title>Skills and Projects</section-title>
<section-content>
<template is="dom-repeat" items="{{careerSkills}}">
<skill-category class="grid-item" title="{{item.header}}" skills="{{item.skills}}"></skill-category>
</template>
<project-showcase class="grid-item" projects="{{projects}}"></project-showcase>
</section-content>
</page-section>
</template>
The data itself is provided elsewhere and is irrelevant. The issue I am running into is that both dom-bind and dom-repeat seem to create local dom and put the result inside of it.
To create my grid, I need to access both the container for the grid, which will be the section-content element and the grid items, which are the skill-category elements inside the dom-repeat template.
If they all resided in the same document, I think could do (I am new to masonry, so this might not actually work):
document.addEventListener('WebComponentsReady', function () {
$('#resume section-content').masonry({
columnWidth: $('#resume skill-category')[0],
itemSelector: 'skill-category',
isFitWidth: true
});
});
But the queries don't seem to work because presumably the elements I need are hidden away from the main document in the shadow dom.
I was able to get access to the content inside #resume-container via:
Polymer.dom(document.querySelector('#resume-container')).node.content
However, I still can't get to the skill-category elements in the dom-repeat. This is getting kind of pedantic and I'm not even sure if it will work when masonry tries to do the positioning.
Is there a better way to go about this?
To be clear, this question is about how to properly gain reference to the content distributed inside of template helpers, but I would also appreciate any general advice to using polymer to do this sort of thing, where a custom element isn't exactly what I'm looking for since I'm only going to use the template in one spot and shadow dom is more hassle than help, but I need the data-binding.
I am still experimenting how far I can go with building a widget in Polymer and I am stuck in one place. I want to create an element with children that are also polymer elements. Something like:
<my-view name="Hello">
<my-child-view attribute="test">Test</my-child-view>
<my-child-view attribute="test1">Test2</my-child-view>
<my-child-view attribute="test2">Test3</my-child-view>
<my-child-view attribute="test3"></my-child-view>
<my-other-child-view var="test5"></my-other-child-view>
</my-view>
I want to get children that are the tag name "my-child-view". Using this.children takes all the children. Is there a native polymer functionality like this.getChildrenWithQuery('my-child-view')? Or should I just loop through children and check the ones with tag name myself?
this.querySelectorAll('my-child-view')
Native JS DOM! Yes, querySelectorAll() works on elements as well as the document.
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
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/