将多个鼠标悬停事件应用于相邻(连接)节点
Posted
技术标签:
【中文标题】将多个鼠标悬停事件应用于相邻(连接)节点【英文标题】:apply several mouseover events to neighboring (connected) nodes 【发布时间】:2013-05-27 07:20:18 【问题描述】:我有一个网络图(力导向图)、一个散点图和一个表,它们都是相互关联的(参见jsFiddle)。我的互连按照我希望它们用于鼠标悬停事件的方式工作。我想修改我的代码,以便当我将鼠标悬停在网络图中的一个节点上时,不仅突出显示鼠标悬停的节点(以及它在散点图和表格中的连接),而且它的直接相邻节点也突出显示(以及作为它们在散点图和表格中的连接)。
我查看了Highlight selected node, its links, and its children in a D3 force directed graph 中的信息以寻求帮助。一路上的某个地方(不确定在哪里)我找到了一个帮助定义连接节点的函数示例,isConnected()
。
function isConnected(a, b)
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
我想将此功能合并到我的鼠标悬停事件中,可能带有if()
语句,以便我可以做所有我想要的“突出显示”。但是,我是 D3 和 js 新手,不知道如何设置。
下面是我想修改的代码(来自jsFiddle)的sn-p。如有任何建议或指向其他示例,我将不胜感激。
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", function(d) return "node " + d.name + " " + d.location; )
.call(force.drag)
.on("mouseover", function(d)
// I would like to insert an if statement to do all of these things to the connected nodes
// if(isConnected(d, o))
d3.select(this).select("circle").style("stroke-width", 6);
d3.select(this).select("circle").style("stroke", "orange");
d3.select(this).select("text").style("font", "20px sans-serif");
d3.selectAll("rect." + d.location).style("stroke-width", 6);
d3.selectAll("rect." + d.location).style("stroke", "orange");
d3.selectAll("text." + d.location).style("font", "20px sans-serif");
d3.selectAll("tr." + d.name).style("background-color", "orange");
//
)
.on("mouseout", function(d)
// if(isConnected(d, o))
d3.select(this).select("circle").style("stroke-width", 1.5);
d3.select(this).select("circle").style("stroke", "gray");
d3.select(this).select("text").style("font", "12px sans-serif");
d3.selectAll("rect." + d.location).style("stroke-width", 1.5);
d3.selectAll("rect." + d.location).style("stroke", "gray");
d3.selectAll("text." + d.location).style("font", "12px sans-serif");
d3.selectAll("tr." + d.name).style("background-color", "white");
//
);
【问题讨论】:
【参考方案1】:在另一种情况下,我会将我的可视对象放入图形数据结构中并对其进行导航以有效地更新适当的项目。但这是 d3,所以我们将做同样的事情,但我们将使用 d3 选择而不是我们创建的图形数据结构(它可以像图形,但为此它们看起来更像数组)。从算法上讲,这种方法效率不高,但我们的图很小。
所以向后工作,我需要一个仅包含所选节点的相邻节点的选择 节点。我将通过选择所有圆圈然后使用 d3 选择过滤器方法将其减少到只有那些是邻居的圆圈来做到这一点。
当然,我需要邻居列表,但是一些不错的 js 数组方法可以轻松解决这个问题。最终的相关代码(鼠标悬停)甚至没有那么长——但我添加了一堆 cmets:
// Figure out the neighboring node id's with brute strength because the graph is small
var nodeNeighbors = graph.links.filter(function(link)
// Filter the list of links to only those links that have our target
// node as a source or target
return link.source.index === d.index || link.target.index === d.index;)
.map(function(link)
// Map the list of links to a simple array of the neighboring indices - this is
// technically not required but makes the code below simpler because we can use
// indexOf instead of iterating and searching ourselves.
return link.source.index === d.index ? link.target.index : link.source.index; );
// Reset all circles - we will do this in mouseout also
svg.selectAll('circle').style('stroke', 'gray');
// now we select the neighboring circles and apply whatever style we want.
// Note that we could also filter a selection of links in this way if we want to
// Highlight those as well
svg.selectAll('circle').filter(function(node)
// I filter the selection of all circles to only those that hold a node with an
// index in my listg of neighbors
return nodeNeighbors.indexOf(node.index) > -1;
)
.style('stroke', 'orange');
你也可以试试fiddle
我认为与此处相关的重要 d3 概念是,当您将数据与元素相关联时(通常在选择上使用 data() 或 datum() 方法),那么数据会与该元素保持一致,并且任何未来的选择都将始终使用它.
要链接其他方面,您可以以类似的方式提取这些属性并通过 d3 链接它们。例如,对于可以添加到鼠标悬停的位置矩形:
var nodeLocations = graph.links.filter(function(link)
return link.source.index === d.index || link.target.index === d.index;)
.map(function(link)
return link.source.index === d.index ? link.target.location : link.source.location; );
d3.selectAll("rect").filter(function(node) return nodeLocations.indexOf(node.location) > -1; ) .style("stroke", "cyan");
【讨论】:
这很有帮助,但它仅将邻居突出显示应用于网络图。我还想突出显示表格和地图中的相应位。因此,例如,当我将鼠标悬停在 GroupA 上时,我希望 Jim、Sally 和 Tom 在网络图中(1)突出显示圆圈(您的代码解决了这个问题),(2)突出显示表格中的行,以及(3)突出显示地图中的矩形。你能帮我完成第 2 部分和第 3 部分吗? 它看起来像您的矩形,也许您的表格与 D3 关联数据的方式不同。但是无论如何你都没有理由不能使用 d3 选择。诀窍是使用相邻节点索引数组来实际获取节点列表或正确属性列表以突出显示其他项目。那么你应该能够应用相同的逻辑。 你能告诉我如何做到这一点吗?我试过d3.selectAll("rect." + d.location).filter(function(node) return nodeNeighbors.indexOf(node.index) > -1; ) .style("stroke", "cyan");
,但显然没有用。
我进行了编辑,您也可以查看here 诀窍是,在您的组节点上,我认为您的“d.location”返回的内容不正确。而且我知道您的映射矩形不包含索引。我喜欢添加诸如“console.log(node);”之类的东西进入选择回调函数,看看最终会发生什么。您可以随时在浏览器开发人员工具中打开“显示控制台”以查看它吐出的内容。 IT 让查看正在发生的事情变得更加容易。
酷。谢谢。我试图用表格模仿这种方法(使用名称而不是位置),但它不起作用,我在浏览器中看不到任何错误。 d3.selectAll("tr").filter(function(node) return nodeNames.indexOf(node.name) > -1; ).style("background-color", "cyan");
你能看到我错过了什么吗?另外,我不清楚在哪里放置“console.log(node):”语句。我在 .filter() 大括号 中返回之前尝试过,但没有成功。【参考方案2】:
我构建的这个东西是通过自我网络功能实现的:
https://gist.github.com/emeeks/4588962
添加一个 .on("mouseover", findEgo) 到你的节点,只要你有某种识别 uid 属性,以下应该可以工作,如果不方便,你可以在加载节点时生成该属性.这有点矫枉过正,因为它允许 n 度自我网络,并为其他网络分析功能创建一个聚合表,但基本功能将为您提供您想要的,您或其他用户可能会发现这方面很有用:
function findEgo(d)
var computedEgoArray = findEgoNetwork(d.id, 1, false,"individual");
d3.selectAll("circle.node").style("fill", function(p) return p.id == d.id ? "purple" : computedEgoArray.indexOf(p.id) > -1 ? "blue" : "pink")
function findEgoNetwork(searchNode, egoNetworkDegree, isDirected, searchType)
var egoNetwork = ;
for (x in nodes)
if (nodes[x].id == searchNode || searchType == "aggregate")
egoNetwork[nodes[x].id] = [nodes[x].id];
var z = 0;
while (z < egoNetworkDegree)
var thisEgoRing = egoNetwork[nodes[x].id].slice(0);
for (y in links)
if (thisEgoRing.indexOf(links[y].source.id) > -1 && thisEgoRing.indexOf(links[y].target.id) == -1)
egoNetwork[nodes[x].id].push(links[y].target.id)
else if (isDirected == false && thisEgoRing.indexOf(links[y].source.id) == -1 && thisEgoRing.indexOf(links[y].target.id) > -1)
egoNetwork[nodes[x].id].push(links[y].source.id)
z++;
if (searchType == "aggregate")
//if it's checking the entire network, pass back the entire object of arrays
return egoNetwork;
else
//Otherwise only give back the array that corresponds with the search node
return egoNetwork[searchNode];
【讨论】:
以上是关于将多个鼠标悬停事件应用于相邻(连接)节点的主要内容,如果未能解决你的问题,请参考以下文章