I've searched through the myriad parent/child array/object/whatever questions here and elsewhere and haven't been able to solve my issue. I know this is a bit long, but I wanted to ensure I'm providing enough info to you guys.
Here's what I want to do:
I have a number of <div>-delineated items on the page with parent/child relationships, generated via php from my database
I want to use these items as the data source for a D3.js Dendrogram (a node-link tree diagram http://mbostock.github.com/d3/ex/cluster.html)
I'm storing them with left/right nested set values but also parentID values, so I can add ID, parentID, rgt, lft and depth attributes to the <div> elements, so I should have available whatever's needed to generate the parent/child relationships on the client side
For various reasons, instead of creating a JSON file on the server side to use as the data source, I need to create it on the client side based on the attributes in #3
I've had difficulty getting various suggested javascript functions to work and all the D3 examples I've found use either a preexisting JSON file or generated math-based file, not attributes of elements already on the page
Here is an example of what already works for me with the D3 Dendrogram, but it's not generated dynamically:
var tree3 =
{"sid": "1", "children": [
{"sid": "2", "children": [
{"sid": "5", "children": [
{"sid": "75"},
{"sid": "85", "children": [
{"sid": "87"}, ...
To give you an idea of where these attributes are in the DOM, I originally tried the below, but of course it doesn't generate any hierarchy:
function tree() {
var tree=[];
$("article").each(function(){
tree.push({
sid:$(this).attr("sid"),
l:$(this).attr("l"),
r:$(this).attr("r"),
pid:$(this).attr("pid")
});
});
return tree;
}
I've been messing around unsuccessfully with variants of the below to get a nested array:
function tree2() {
$("article").(function(d) {
return d.parent().attr("pid") === 0;
}, function(parent, child) {
return parent.attr("pid") === child.parent().attr("sid");
}).toArray();
}
So, I'm driving myself crazy trying to create the javascript array nested correctly, but it's dawned on me that I may not need to and that D3's data selectors and methods could be sufficient. Could you please help me with the code to:
Pull the needed attributes to generate the parent/child relationship within a D3 function ("sid" is the identifier) or, if this isn't possible,
Create the needed array or array-like object in javascript for use by D3 (still with "sid" as the identifier).
Thanks in advance.
You need to get recursive! Basically the trick is to pass the current parent in as you go, which changes the context and allows you to walk down the tree.
Update: Working fiddle.
Assuming your HTML structure is something like this:
<div sid="1" pid="">
<div sid="1.1" pid="1">
<div sid="1.1.1" pid="1.1">
</div>
</div>
</div>
You could do something like this:
var _json = {};
function addTreeNode(div, parentObj) {
var childObj = {
sid: $(div).attr("sid"),
pid: $(div).attr("pid")
}
// add this to it's parent in the JSON hierarchy
if (!parentObj.children) parentObj.children = [];
parentObj.children.push(childObj);
// keep adding for all children div's
$(div).find("div").each(function() {
addTreeNode(this, childObj);
});
}
// start at the roots, it will magically work it's way out to the leaves
$("body > div").each(function(){
addTreeNode(this, _json);
});
console.log(_json);
Note that if your tree is big enough, you will cause stack overflows, especially in IE. In that case, you'll need to switch this over from recursion to iteration. It's not as pretty that way, though.
Related
I'm get a json object from an Ajax request like this:
"items":[
{
"id":923,
"id_str":"608475747557236737",
"uid":407388514,
"date":"Wed Jun 10 03:28:17 +0000 2015",
"status":0,
},
{
"id":923,
"id_str":"608475747557236737",
"uid":407388514,
"date":"Wed Jun 10 03:28:17 +0000 2015",
"status":0,
}
]
I loop the json object and generate HTML elements.
My question is if Is best store the json information in each HTML element such us: data-prop1="", data-prop2="" etc, o i keep the data in a javascript var like array?
The information of the HTML element will be send via Ajax request to the server again, so i want to store and restore to send.
From a performance point of view it is far better to store it in a variable instead of directly on your HTML elements
DOM operations are expensive (selecting/accessing that HTML element
which holds the data).
It's also the most cross-browser compatible way
since data attributes only apply on HTML5 browsers
It also easier to console.log it, and inspect it in your Developer tools.
Personally, I use the data-* attributes only when little information (such as a couple of attributes) is needed to be stored for each element and that information would be send by a direct click/hover event on the element itself - Otherwise it doesn't make much sense to me to store it within the DOM.
None is stopping you from saving whole JSON's on the DOM, if you find it easier - However if you keep on doing it you will end up with a very convoluted markup and a pattern that just looks terrible
I am working with a similar scenario currently and I've chosen to implement a model on the client's side which holds the information, because storing everything in the DOM could be harmful to the overall performance.
In other words I have some HTML code, for example
<div id='element-1' class='element'>foo</div>
<div id='element-2' class='element'>bar</div>
<div id='element-3' class='element'>baz</div>
which corresponds to the recieved data
[{id:1, text:'foo'}, {id:2, text:'bar'}, {id:3, text:'baz'}]
and instead of using DOM I have a model object which holds the data, is accessible from everywhere and has methods to search in the data, render it and so on.
A very simplified example of such object could look like this:
function Model() {
this.data = []; //after recieving the data via Ajax it is stored here
this.find = find;
function find(id) {
//cycle through data and return the correct record
}
this.render = render;
function render(id) {
var obj = find(id);
// find the element with $('#element-'+id) and update it's text
}
this.update = update;
function update(id, text) {
var obj = find(id);
obj.text = text;
}
}
The advantage is that you don't make the DOM heavy and you keep your data in a clean and organized way and the downfall is that you have to keep your displayed data and model data in sync.
I would recommend not to store anywhere in html, as you going to pass elements to another ajax request. So just create variable in javascript and store it temporary.
I am trying to build a web form which will have its initial fields determined based on rows in a database. For example, form 1 might have fields A, B, and C. On the other hand, form 2 might have fields D, E, and F. Additionally, there may be hundreds or even thousands of such forms, each with a unique ID.
Now, each one of these fields (A, B, C, etc) might in turn be made up of other fields, which in turn can be made up of yet even more fields. In other words, some fields are actually collections of fields (to an arbitrary depth).
On top of that, the user of the form can choose to add more instances of some of these fields at runtime. Sometimes the user will choose to add a whole new record (all fields) while at other times they may choose to only add another instance of one of the fields (which may itself contain more fields). Also, I need the user to be able to remove any of these fields that they have added.
I've spent about 30 hours so far along with a colleague of mine coming up with a custom Javascript-based solution that involves building our own tree structure alongside the DOM tree, writing some recursive functions, etc but, as the complexity has mounted, it really seems like we were reinventing the wheel here. This strikes me as a problem that must have already been solved at this point.
I'm not very familiar with jQuery but it sounds like it might potentially be a good solution based on what I've heard about it in the past. In other words, I suspect that this might be a problem that jQuery more or less solves "out of the box." But, my initial research into jQuery (on Stack Overflow and on Google in general) gives me the impression that this isn't the case and that a custom solution, using jQuery, needs to be put together.
My question is, what is the easiest way to achieve what I'm looking for? The solution doesn't even have to use jQuery; I just thought that jQuery might be the best way to do it. I'm not looking for someone to write the code for me, just to point me in the right direction since our solutions so far have looked pretty messy.
If I understand what you're looking for, using jQuery combined with an easy to parse response framework like JSON would probably be what you're looking for. If you're using PHP you can pretty easily create a large nested array of form data and just print it out as JSON like this:
<?php
$form1Array = array(
array('type'=>'text','name'=>'input_a','id'=>'input_a','value'=>'Example default value','label'=>'Input A'),
array('type'=>'text','name'=>'input_b','id'=>'input_b','label'=>'Input B'),
array('type'=>'password','name'=>'input_c','id'=>'input_c','label'=>'Input C'));
$form2Array = array(
array('type'=>'text','name'=>'input_d','id'=>'input_d','label'=>'Input D'),
array('type'=>'text','name'=>'input_e','id'=>'input_e', 'label'=>'Input E'),
array('type'=>'password','name'=>'input_f','id'=>'input_f', 'label'=>'Input F','can_replicate'=>true));
$output = array('form_1'=>array('id'=>'form_1','label'=>'Form 1','elements'=>$form1Array,'can_replicate'=>true),'form_2'=>array('id'=>'form_2','label'=>'Second form','elements'=>$form1Array));
echo json_encode($output,JSON_PRETTY_PRINT);
?>
That will obviously need to be modified to actually construct the structure from your database output, but that should be a fairly simple process. The output of that example will give you a nice JSON output that you can parse using jQuery:
{
"form_1": {
"id": "form_1",
"label": "Form 1",
"elements": [
{
"type": "text",
"name": "input_a",
"id": "input_a",
"value": "Example default value",
"label": "Input A"
},
{
"type": "text",
"name": "input_b",
"id": "input_b",
"label": "Input B"
},
{
"type": "password",
"name": "input_c",
"id": "input_c",
"label": "Input C"
}
],
"can_replicate": true
}
}
After that, you'd just query your php page and assemble the output information into forms and elements:
<!DOCTYPE html5>
<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){ //wait for the whole document to load before modifying the DOM
$.ajax({
url: "test.php",
success: function(data, status, jqXHR) {
var formData = jQuery.parseJSON(data);
$.each(formData, function(id, formInfo){ // function(key, value); In the PHP example, I used the key as the form ID, with the value containing the array of form data
var newForm = $('<form></form>'); //Create an empty form element
newForm.attr('id',formInfo.id); //Set the form ID
$.each(formInfo.elements,function(key, formInput){ //I didn't place a key on the inputs, so the key isn't used here
var newInput = $('<input></input>'); //Create a new empty input
newInput.attr({'id':formInput.id,'type':formInput.type,'name':formInput.name});
if(formInput.value != null){ //If a default value is given
newInput.val(formInput.value); //Set that value
}
if(formInput.label != null){
var newLabel = $('<label></label>');
newLabel.attr('for',formInput.label); //Set the ID that this label is for
newLabel.html(formInput.label); //Set the label text
newForm.append(newLabel);
}
newForm.append(newInput);
if(formInput.can_replicate != null && formInput.can_replicate == true){
//Place code here to insert a button that can replicate the form element
//I'm just going to put a plus sign here
newForm.append('+');
}
newForm.append('<br />');
});
if(formInfo.can_replicate != null && formInfo.can_replicate == true){
//put code here to insert a button that replicates this form's structure
}
if(formInfo.label != null){
var newLabel = $('<label></label>');
newLabel.attr('for',formInfo.id);
newLabel.html(formInfo.label);
$('#form_container').append(newLabel);
}
$('#form_container').append(newForm);
});
}
})
});
</script>
</head>
<body>
<h1>Testing</h1>
<div id="form_container">
</div>
</body>
</html>
After all of that, this is the output the example should produce:
I would recommend checking KnockoutJS. With this Jquery plugin you can dynamically add or remove to you DOM using ko if: comments. Also, you can simply show or hide elements by using the visible data-bind. I would recommend checking out their tutorials the collections sections should give you a good start on how to implement what you are trying to.
I am basically worse then a noob on Raphael. I was wondering how to set the path ID for the following code. I did some googling but couldn't find any way to set ID to the following code, since it uses an array.
var mySVG=['exampleDOMid',400,400,
{
"type":"path","fill":"#C4C5C7","path":"path code here","stroke":"none"
},{
"type":"path","fill":"#EDEDEE","path":"path code here","stroke":"none"
},{
"type":"path","fill":"#5A5B5D","path":"path code here","stroke":"none"
},{
"type":"path","fill":"#231F20","path":"path code here","stroke":"none"
}];
var r = Raphael(mySVG);
I generated the code from http://toki-woki.net/p/SVG2RaphaelJS/ by submitting a SVG file
To elaborate on my comment, it depends how you want to use that id.
1. If you need the ID just for some precedence of events, then just use the position in the array
2. To have something specific, just add this to your path:
path.data("id", id);
Then you can retrieve the id by doing the following:
var index = path.data("id");
Also look into Raphael documentation for data() function for more details.
I previously asked a similar question before, however it seemed that I wasn't being precise enough about my question - hopefully this is.
Many examples of the Tree Layout format their hierarchy data set as this: https://gist.github.com/tchaymore/1249394#file-math_map_compact-json
However I wanted to change the "children" node in the json to "children1" for example
var flare = {
"Name": "Example",
"children1": [
{
"BName":"Ja",
"Email":"",
"children1":[
{
"NickName":"NC",
"id":2
}
],
}
]};
When I had changed the name of that node, I then used the children function:
var tree = d3.layout.tree()
.size([height, width])
.children(function(d) { return d.children1; });
After that I changed all of the "children" references to "children1" & so on.
However, now I have the problem of not being able to collapse the tree back into its parent. I've tried to use this example as a reference, but no luck: http://blog.pixelingene.com/2011/07/building-a-tree-diagram-in-d3-js/
Here's a example of my current problem: http://jsfiddle.net/mEyQW/1/
.. I apologize for the previous confusion
I don't really get what you wanted to do with your collapse() function and the _children1 but if you remove it, then everything works fine:
jsFiddle: http://jsfiddle.net/chrisJamesC/mEyQW/2/
I am using jQuery and jQuery UI, and I am almost new to JavaScript. I would like to set a global variable in the window object so to keep custom data related to multiple jQuery objects. That is, at this time I am using the following (poor) code:
// Given
window.myDataObject = {};
// Then, in the same file, I run multiple times (one time for each 'tag_id_1',
// 'tag_id_2', ..., 'tag_id_N') the following code
var tag = $('#tag_id_N')
myDataObject = { pagination: { page : 1, per_page: 10 } } // This aims to keep pagination data for tags.
Since I am trying to keep data for multiple tags, running the above code multiple times makes the window.myDataObject to be continuously "re-initialized" (making the keeping process inefficient). Because that, I thought to add "namespaced" properties (maybe, namespaced with "something" related to each tag object) to window.myDataObject so that each tag has its own pagination data.
How can I make that? Is that approach a "good" / "proper" way to proceed?
I think you're just looking for the .data() method:
The .data() method allows us to attach data of any type to DOM
elements in a way that is safe from circular references and therefore
from memory leaks.
We can set several distinct values for a single element and retrieve
them later:
First of all you should be using the window object not windows.
Secondly if you want to use multiple tags you could try to do the following:
// Given
windows.taggedObjects = {};
// Then, in the same file, I run multiple times (one time for each 'tag_id_1',
// 'tag_id_2', ..., 'tag_id_N') the following code
var tagId = 'tag_id_N';
// This aims to keep pagination data for tags.
window.taggedObjects[tagId] = { tag: $('#' + tagId), pagination: { page : 1, per_page: 10 } };
To retrieve your data just use the tag id again like so:
alert(window.taggedObjects[tagId]);