I am using force layout of d3.js and I need to show the path to the root in graph. Ex- On click of "Task 10" node it should show the path (Task 10->Task 6-> Task 4-> Task 1). Root will be recognize as same source and target but this info I am passing in data only. My data contain "RootPath" having path info for the node like for "Task 10" RootPath is ["Task 6","Task 4","Task 1"] .
My complete code for the graph can be seen here http://plnkr.co/edit/EvpNC6B5DBWczNXKiL82?p=preview.
I am writing below method so that on click to the particular node it will show the root path. But currently it only show for 1 node. I don't know how to iterate for all other lists of node.
function rootPath(d){
var curNodeDetail = d.details.RootPath;
var source=[],target=[],i=0,j=0;
source[i] = d.name;
target[i] = curNodeDetail[i];
links.style("stroke-opacity", function(o) {
if(source[i] === o.source.name && target[i] === o.target.name){
source[i+1] = curNodeDetail[i];
target[i+1] = curNodeDetail[i+1];
i++;
return 1;
}
else
return 0.1;
})
.attr("marker-end", function(o){
if(source[j] === o.source.name && target[j] === o.target.name){
j++;
return "url(#arrowhead)";
}
else
return "url(#arrowhead2)";
});
}
Turn d.details.rootpath into a d3.set and add the current node, then interrogate each link to see if both ends are part of the set.
This works for your links, but you'll have to do likewise for the nodes -->
var curNodeDetail = d.details.RootPath;
var rootMap = d3.set (curNodeDetail);
rootMap.add(d.name);
links.style("stroke-opacity", function(o) {
return (rootMap.has(o.source.name) && rootMap.has (o.target.name)) ? 1 : 0.1;
})
.attr("marker-end", function(o){
return (rootMap.has(o.source.name) && rootMap.has (o.target.name)) ? "url(#arrowhead)" : "url(#arrowhead2)";
});
}
Below function can be useful :-
function rootPath(d){
var curNodeDetail = d.details.RootPath;
var rootMap = rootPathItinerary(d.name,curNodeDetail);
links.style("stroke-opacity", function(o) {
return (rootMap.has(o.source.name) && (rootMap.get(o.source.name) === o.target.name)) ? 1 : 0.1;
})
.attr("marker-end", function(o){
return (rootMap.has(o.source.name) && (rootMap.get(o.source.name) === o.target.name)) ? "url(#arrowhead)" : "url(#arrowhead2)";
});
}
function rootPathItinerary(node,rootPathDet){
var i=0;
var map = d3.map();
map.set(node,rootPathDet[i]);//10,6
while(i+1 <rootPathDet.length){ //6,4,1
map.set(rootPathDet[i],rootPathDet[i+1]) ; //6,4 ; 4,1
i++;
}
return map;
}
Related
I'm using the Sigma.js library to create a webapp on top of a Neo4j graph database.
So, I have my graph with nodes and edges and I would like to expand nodes by double click event with javascript.
Can you help me to do this?
Thank you so much
sigma.neo4j.cypher(
{url :'http://localhost:7474', user: 'neo4j', password: 'neo4j'},
'MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 100' ,
{container:'graph-container'},
function (s) {
s.graph.nodes().forEach(function (node){
if (node.neo4j_labels[0] === "Movies"){
node.label = node.neo4j_data['movie'];
node.color = '#68BDF6';
node.size = 26;
node.type = "star";
}
else if (node.neo4j_labels[0] === "Personn"){
node.label = node.neo4j_data['personn'];
node.type = "diamond";
node.color = '#303A2B';
node.size = 12;
}
console.log(s.graph.nodes());
s.settings('touchEnabled', true);
s.settings('mouseEnabled', true);
s.settings('defaultEdgeLabelSize', 18);
s.settings('defaultNodeLabelSize', 18);
s.settings('animationsTime', 1000);
s.graph.edges().forEach(function(e) {
//e.type = 'curvedArrow';
//e.color='#F00';
e.originalColor = e.color;
e.type = "curvedArrow";
e.size=2;
// e.label = neo4j_type;
//console.log(s.graph.edges())
});
s.refresh();
});
You have to bind the sigma event doubleClickNode like this :
s.bind("doubleClickNode", function(e) {
var node = e.data.node;
var query = 'MATCH (n)-[r]-(m) WHERE id(n)=' + node.id+ ' RETURN n,r,m';
sigma.neo4j.cypher(
neo4jConfig,
query,
function(graph) {
// adding node if not existing
graph.nodes.forEach(function (node){
if(s.graph.nodes(node.id) === undefined) {
s.graph.addNode(node);
}
});
// adding edge if not existing
graph.edges.forEach(function (edge){
if(s.graph.edges(edge.id) === undefined) {
s.graph.addEdge(edge);
}
});
// apply style
applyStyle(s);
}
);
});
My complete example is available here : https://jsfiddle.net/sim51/qkc0g58o/34/
Before to use it, you need to accept Neo4j SSL certificate, by opening this link https://localhost:7473/browser and adding a security exception
I am currently adding a feature to a genealogy website I am currently working on which is based on the Webtrees open source software.
So here is where I am at:
So originally, the software allows the user to press the individual node to expand/compress its content. And that works through the following code:
TreeViewHandler.prototype.expandBox = function(j, i) {
if (jQuery(i.target).hasClass("tv_link")) {
return !1
}
j = jQuery(j, this.treeview);
var p = j.parent(),
o = j.attr("abbr"),
n = this,
l, k;
if (p.hasClass("detailsLoaded")) {
k = p.find(".collapsedContent"), l = p.find(".tv_box:not(.collapsedContent)")
} else {
l = j;
k = j.clone();
p.append(k.addClass("collapsedContent").css("display", "none"));
var m = this.loadingImage.find("img").clone().addClass("tv_box_loading").css("display", "block");
j.prepend(m);
n.updating = !0;
n.setLoading();
j.load(n.ajaxUrl + "getDetails&pid=" + o, function() {
"function" === typeof CB_Init && CB_Init();
j.css("width", n.zoom / 100 * n.boxExpandedWidth + "px");
m.remove();
p.addClass("detailsLoaded");
n.setComplete();
n.updating = !1
})
}
j.hasClass("boxExpanded") ? (l.css("display", "none"), k.css("display", "block"), j.removeClass("boxExpanded")) : (l.css("display", "block"), k.css("display", "none"), l.addClass("boxExpanded"));
this.getSize();
return !1
};
What I'm trying to do is I'm trying to make the button I placed on the top left, which is to expand/compress ALL boxes in one click work. The one in showing/hiding females already work.
$(document).ready(function(){
var female = false
$(".tv_hidefemales").click(function(){
if (female == false) {
$(".tvF").hide();
female = true
} else {
$(".tvF").show();
female = false
}
});
$(".tv_expandCompress").click(function(){
tvHandler.expandBox($(".tv"), 0);
});
});
I figured that I can just take advantage of a looping jQuery .click() function.
I'm trying to shorten my function that contains a number of if statements into a loop function but just can't seem to get it to work. It is currently working in Google Tag Manager as a Custom Javascript Macro.
I'm not a developer or very good at Javascript so any help will be greatly appreciated.
Code:
function()
{
if(document.getElementById('bfPage1').style.display !== "none")
{return document.getElementById('bfPage1').getAttribute('id');
}
else
if(document.getElementById('bfPage2').style.display !== "none")
{return document.getElementById('bfPage2').getAttribute('id');
}
else
if(document.getElementById('bfPage3').style.display !== "none")
{return document.getElementById('bfPage3').getAttribute('id');
}
else
{return document.getElementById('bfPage4').getAttribute('id')
}
}
Thanks in advance.
TLDR:
// with jQuery
var allPages = $('[id^=bfPage]')
var activeId = allPages.filter(':visible').attr('id')
// with ES6
const allPages = [].slice.apply(document.querySelectorAll('[id^=bfPage]'));
let activeId = allPages.find( e => e.style.display !== "none").id;
First of all you do not need to nest your ifs using else statements, because you are using return statement inside each of them. So after first refactoring you are going to have:
function()
{
if(document.getElementById('bfPage1').style.display !== "none")
{return document.getElementById('bfPage1').getAttribute('id');
}
if(document.getElementById('bfPage2').style.display !== "none")
{return document.getElementById('bfPage2').getAttribute('id');
}
if(document.getElementById('bfPage3').style.display !== "none")
{return document.getElementById('bfPage3').getAttribute('id');
}
if(document.getElementById('bfPage4').style.display !== "none")
{return document.getElementById('bfPage4').getAttribute('id')
}
}
Next step is to notice that that each time you are quering element twice:
function()
{
var page
page = document.getElementById('bfPage1')
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
page = document.getElementById('bfPage2')
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
page = document.getElementById('bfPage3')
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
page = document.getElementById('bfPage4')
if(page.style.display !== "none")
{
return page.getAttribute('id')
}
}
Now you clearly see that you can use loop instead of repeating statements
function()
{
var pages = ['bfPage1','bfPage2','bfPage3','bfPage4']
for (var i in pages) {
page = document.getElementById(pages[i])
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
}
}
Oh and you already know id
function()
{
var pages = ['bfPage1','bfPage2','bfPage3','bfPage4']
for (var i in pages) {
page = document.getElementById(pages[i])
if(page.style.display !== "none")
{
return pages[i];
}
}
}
Which is quite nice.
You can use jquery and make one-liner out of it:
$('[id^=bgPage]:visible]').attr('id')
This is good result but lets think why do we need to do this in the first place?
You have some logic to show and hide elements of the page and you want to check which one is visible.
Instead of doing that with styles of elements, lets do two things.
add new class for every element .bfPage
lets add class visible or active to the only visible element
This way your code to get id will be changed to:
$('.bfPage.active').attr('id')
Which in case of real application will be split on two parts:
somewhere in the beginning you will have
var allPages = $('.bfPages')
and somewhere where you need to find that element
var activeId = allPages.filter('.active').attr('id')
Of course if it is not the case you still can improve performance caching all bfPages
var allPages = $('[id^=bfPage]')
...
var activeId = allPages.filter(':visible').attr('id')
You could create an array of all your element ids and loop over that and return the id of the first visible one:
var pageIds = ['bfPage1', 'bfPage2', 'bfPage3', 'bfPage4'];
for(var i=0;i<pageIds.length;i++){
var elemId = pageIds[i];
if(document.getElementById(elemId).style.display !== 'none')
{
return elemId;
}
}
Is this what you're trying to do?
function(){
for (var i = 0; i < 4; i++){
var id = "bfPage" + i;
if(document.getElementById(id).style.display !== "none"){
return id;
}
}
}
Say I've selected a span tag in a large html document. If I treat the entire html document as a big nested array, I can find the position of the span tag through array indexes. How can I output the index path to that span tag? eg: 1,2,0,12,7 using JavaScript.
Also, how can I select the span tag by going through the index path?
This will work. It returns the path as an array instead of a string.
Updated per your request.
You can check it out here: http://jsbin.com/isata5/edit (hit preview)
// get a node's index.
function getIndex (node) {
var parent=node.parentElement||node.parentNode, i=-1, child;
while (parent && (child=parent.childNodes[++i])) if (child==node) return i;
return -1;
}
// get a node's path.
function getPath (node) {
var parent, path=[], index=getIndex(node);
(parent=node.parentElement||node.parentNode) && (path=getPath(parent));
index > -1 && path.push(index);
return path;
}
// get a node from a path.
function getNode (path) {
var node=document.documentElement, i=0, index;
while ((index=path[++i]) > -1) node=node.childNodes[index];
return node;
}
This example should work on this page in your console.
var testNode=document.getElementById('comment-4007919');
console.log("testNode: " + testNode.innerHTML);
var testPath=getPath(testNode);
console.log("testPath: " + testPath);
var testFind=getNode(testPath);
console.log("testFind: " + testFind.innerHTML);
Using jquery:
var tag = $('#myspan_id');
var index_path = [];
while(tag) {
index_path.push(tag.index());
tag = tag.parent();
}
index_path = index_path.reverse();
Using the DOM
node = /*Your span element*/;
var currentNode = node;
var branch = [];
var cn; /*Stores a Nodelist of the current parent node's children*/
var i;
while (currentNode.parentNode !== null)
{
cn = currentNode.parentNode.childNodes;
for (i=0;i<cn.length;i++)
{
if (cn[i] === currentNode)
{
branch.push(i);
break;
}
}
currentNode = currentNode.parentNode;
}
cn = document.childNodes;
for (i=0;i<cn.length;i++)
{
if (cn[i] === currentNode)
{
branch.push(i);
break;
}
}
node.innerHTML = branch.reverse().join(",");
composedPath for native event.
(function (E, d, w) {
if (!E.composedPath) {
E.composedPath = function () {
if (this.path) {
return this.path;
}
var target = this.target;
this.path = [];
while (target.parentNode !== null) {
this.path.push(target);
target = target.parentNode;
}
this.path.push(d, w);
return this.path;
};
}
})(Event.prototype, document, window);
use:
var path = event.composedPath()
I write this function for getting xpath location:
JQuery (this function get xpath):
function getXPath(node, path) {
path = path || [];
if(node.parentNode) {
path = getXPath(node.parentNode, path);
}
if(node.previousSibling) {
var count = 1;
var sibling = node.previousSibling
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
sibling = sibling.previousSibling;
} while(sibling);
if(count == 1) {count = null;}
} else if(node.nextSibling) {
var sibling = node.nextSibling;
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
var count = 1;
sibling = null;
} else {
var count = null;
sibling = sibling.previousSibling;
}
} while(sibling);
}
if(node.nodeType == 1) {
path.push(node.nodeName.toLowerCase() + (node.id ? "[#id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
}
return path;
};
and I have an iframe:
<iframe id="frameID" width="100%" src="http://www.example.com"></iframe>
But Now I have a problem how to integrate my jquery function and where to when i click somewhere in Iframe get xpath result?
You wrote that function? -- That code was written nearly 5 years ago on DZone: http://snippets.dzone.com/posts/show/4349 -- This includes instructions for usage for the function.
You can only do that when the iframe location is in the same domain as the page embedding it.
If so, get the Document from the iframe (document.getElementById('frameId').contentDocument, see MDN) and add a click event listener to it. The event you're receiving will contain a reference to the target node.