如何使 D3 强制布局中的标签和节点可单击以导航到 URL?

2023-11-25

我正在使用 D3 使用基于力的布局,并且想知道当我单击节点或标签时是否可能会自动转到存储在该节点/标签中的 url?如果是的话,我怎样才能实现这一目标?

enter image description here

这就是图表的实际外观(标签指示 URL 本身)

enter image description here

我正在分享我用来生成 D3 图的 JavaScript 代码。在代码中,我当前使用虚拟数据,这些数据实际上将被 URL 替换。

document.addEventListener('DOMContentLoaded', function () {
    drawVisual();
});

function drawVisual()
{
    //document.getElementById('myMSG').innerHTML = "last name"+localStorage.lastname;
    /*var getArr = [];
    getArr = JSON.parse(localStorage.getItem('storeArray'));
    document.getElementById('myMSG').innerHTML = getArr[1].parentURL;*/


    //var w = 1024, h = 768;

    var w=window.innerWidth
    || document.documentElement.clientWidth
    || document.body.clientWidth;

    var h=window.innerHeight
    || document.documentElement.clientHeight
    || document.body.clientHeight;
    //var w = 1024, h = 768;

    //var vis = d3.select("#tab_5_contents").append("svg:svg").attr("width", w).attr("height", h);
    var vis = d3.select("#forcedLayoutGraph").append("svg:svg").attr("width", w).attr("height", h);

            var QueuedORG = [];
            //QueuedORG = JSON.parse(localStorage.getItem('storeArray'));
            QueuedORG.push({url: "Root", parentURL: "Root", used:0});
            QueuedORG.push({url: "a", parentURL: "Root", used:0});
            QueuedORG.push({url: "b", parentURL: "Root", used:0});
            QueuedORG.push({url: "c", parentURL: "Root", used:0});
            QueuedORG.push({url: "d", parentURL: "Root", used:0});
            QueuedORG.push({url: "e", parentURL: "a", used:0});
            QueuedORG.push({url: "f", parentURL: "a", used:0});
            QueuedORG.push({url: "g", parentURL: "a", used:0});
            QueuedORG.push({url: "h", parentURL: "a", used:0});
            QueuedORG.push({url: "p", parentURL: "b", used:0});
            QueuedORG.push({url: "q", parentURL: "b", used:0});
            QueuedORG.push({url: "r", parentURL: "b", used:0});
            QueuedORG.push({url: "x", parentURL: "c", used:0});
            QueuedORG.push({url: "y", parentURL: "x", used:0});
            QueuedORG.push({url: "y", parentURL: "c", used:0});
            QueuedORG.push({url: "x", parentURL: "a"});
            QueuedORG.push({url: "y", parentURL: "b"});


            var nodes = [];

            var labelAnchors = [];
            var labelAnchorLinks = [];
            var links = [];

            for(var i = 0; i < QueuedORG.length; i++) 
            {
                var nodeExists = 0;

                //check to see if a node for the current url has already been created. If yes, do not create a new node
                for(var j = 0; j < nodes.length; j++)  
                {
                    if(QueuedORG[i].url == nodes[j].label)
                        nodeExists = 1;

                }

                if (nodeExists == 0)
                {
                    var urlLabel = QueuedORG[i].url;
                    //remove 'http://' part
                    /*urlLabel = urlLabel.split("http://")[1];
                    if(urlLabel.match("www"))
                    urlLabel = urlLabel.split("www.")[1];
                    var rest = urlLabel.split("\.")[1];
                    urlLabel = urlLabel.split("\.")[0];*/

                    var node = {
                        label : QueuedORG[i].url,
                        category : QueuedORG[i].category
                    };
                    nodes.push(node);
                    labelAnchors.push({
                        node : node
                    });
                    labelAnchors.push({
                        node : node
                    });
                }
            };

            for(var i=0;i<nodes.length; i++)
            {
                console.log("node i:"+i+nodes[i]+"\n");
                console.log("labelAnchor i:"+i+labelAnchors[i]+"\n");
            }

            //To create links for connecting nodes
            for(var i = 0; i < QueuedORG.length; i++) 
            {
                var srcIndx = 0, tgtIndx = 0;
                for(var j = 0; j < nodes.length; j++)
                {
                    if( QueuedORG[i].url == nodes[j].label ) //to find the node number for the current url
                    {
                        srcIndx = j;
                    }

                    if( QueuedORG[i].parentURL == nodes[j].label ) //to find the node number for the parent url
                    {
                        tgtIndx = j;
                    }
                }
                //console.log("src:"+srcIndx+" tgt:"+tgtIndx);

                //connecting the current url's node to the parent url's node
                links.push({
                    source : srcIndx,
                    target : tgtIndx,
                    weight : 1,
                });

                labelAnchorLinks.push({
                    source : srcIndx * 2,
                    target : srcIndx * 2 + 1,
                    weight : 1
                });
            };

            var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).charge(-10000).linkStrength(function(x) {
                return x.weight * 10                                            // charge is for inter-node repel, link distance is node-node distance 
            });
            force.linkDistance(function(d) {
                return d.weight * 100;
            });

            force.start();

            var force2 = d3.layout.force().nodes(labelAnchors).links(labelAnchorLinks).gravity(0).linkStrength(10).charge(-500).size([w, h]);   //charge is for inter-label repel, link distance is node-label distance
            force2.linkDistance(function(d) {
                return d.weight * 10;
            });

            force2.start();

            var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke", "#CCC");

            var colors = {"1": "black", "2": "blue", "3": "red"};           // 1=root node 2=blog nodes 3=.org nodes
            var shape = {"1": "diamond", "2": "cross", "3": "circle"};

            var node = vis.selectAll("g.node").data(force.nodes()).enter().append("path").attr("class", "node").call(force.drag);
        //node.append("circle").attr("r", 5).style("stroke", "#FFF").style("stroke-width", 3).attr("class", function(d) {return "node category"+d.category});

            node.attr("d", d3.svg.symbol().type(function(d) {return shape[d.category];})).style("stroke", "#FFF").style("fill", function(d){ return colors[d.category];});

            var anchorLink = vis.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");

            var anchorNode = vis.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode");
            anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
            anchorNode.append("svg:text").text(function(d, i) {
                return i % 2 == 0 ? "" : d.node.label
            }).style("fill", "#555").style("font-family", "Arial").style("font-size", 12);

            var updateLink = function() {
                this.attr("x1", function(d) {
                    return d.source.x;
                }).attr("y1", function(d) {
                    return d.source.y;
                }).attr("x2", function(d) {
                    return d.target.x;
                }).attr("y2", function(d) {
                    return d.target.y;
                });
            }

            var updateNode = function() {
                this.attr("transform", function(d) {
                    return "translate(" + d.x + "," + d.y + ")";
                });

            }

            force.on("tick", function() {

                force2.start();

                node.call(updateNode);

                anchorNode.each(function(d, i) {
                    if(i % 2 == 0) {
                        d.x = d.node.x;
                        d.y = d.node.y;
                    } else {
                        var b = this.childNodes[1].getBBox();

                        var diffX = d.x - d.node.x;
                        var diffY = d.y - d.node.y;

                        var dist = Math.sqrt(diffX * diffX + diffY * diffY);

                        var shiftX = b.width * (diffX - dist) / (dist * 2);
                        shiftX = Math.max(-b.width, Math.min(0, shiftX));
                        var shiftY = 5;
                        this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
                    }
                });

                anchorNode.call(updateNode);

        link.call(updateLink);
        anchorLink.call(updateLink);

    });
}

一般来说,您可以使用以下命令向 D3.js 中的 SVG 元素添加点击事件

.on('click', function(d, i) {
  window.location.href = d.url;
})

d是数据对象并且i位于集合内 d 的索引中。

只需将该单击处理程序添加到您的文本节点以及您的节点(路径)节点,就像这个小提琴一样http://jsfiddle.net/jNyrf/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使 D3 强制布局中的标签和节点可单击以导航到 URL? 的相关文章

随机推荐

  • .NET 捕获、分组、匹配之间的差异

    我正在使用 NET Regex 类型制作一个小型应用程序 而 捕获 分组和匹配 类型完全让我困惑 我从未见过如此丑陋的解决方案 有人能为我解释一下它们的用法吗 非常感谢 这是一个比 Dav 引用的文档中的例子更简单的例子 string s0
  • 如何在 PowerQuery 中引用单元格的值

    我有多个 Power Query 查询 我想将这些查询提供给 Excel 文件中的单元格的值 在本例中 为源文件名的完整路径 有什么办法可以将其导入到 Power Query 中吗 这可以使用 PowerQuery 中的命名范围和自定义函数
  • 如何使用 TryParseHtmlString 将十六进制转换为颜色(RGBA)

    如何使用十六进制值更改 Unity 中的按钮颜色 我尝试了这个 但它不起作用 也许我在这里犯了一个错误 btn image color ColorUtility TryParseHtmlString DADADAFF out color 您
  • 如何通过 PHP 获取页面正文中的 URL 参数?

    在 Drupal 站点上 为页面正文内容启用了 PHP 代码 如何在 PHP 代码中获取参数及其值 例如 我想得到ref and 33002 from http example com node 1 ref 33002 在下面的代码中 我可
  • RPres 中的代码框大小和字体大小

    我过去经常使用 knitr 和 beamer 来制作幻灯片 我现在在 R 演示文稿中注意到一件事 代码框和代码字体大小的动态大小似乎都丢失了 使用 knit beamer 我可以轻松地使用 size 参数更改字体大小 例如 size foo
  • 如何将单个反斜杠放入 ES6 模板文字输出中?

    我正在努力获取 ES6 模板文字来生成单个反斜杠及其结果 gt s s gt s s gt s s gt s s gt u005Cs s 通过检查 Node REPL 上的值 而不是使用打印它 使用 Node 8 9 1 和 10 0 0
  • ios:如何使用 StoryBoards 从 ViewController 打开选项卡栏控制器

    我正在这样做 并且想要从登录页面打开选项卡栏控制器 如果是第一次并且已经登录 然后传递登录页面并在 Spalsh 之后打开选项卡栏控制器 意味着如果用户第一次登陆应用程序 时间顺序应该是这样的 1 飞溅 2 登录页面 3 成功登录后 打开具
  • django forms:在单个表单中编辑多组相关对象

    我正在尝试做一些应该很常见的事情 以单一形式添加 编辑一堆相关模型 例如 Visitor Details Select destinations and activities Miami swimming clubbing sunbathi
  • Url 编码和 HTML 编码之间的区别

    和有什么区别网址编码 and a HTML 编码 HTML 编码对 HTML 文档中使用的字符串中的特殊字符进行转义 以防止与 HTML 元素混淆 例如更改
  • Python 异步任务排序

    我有一个关于python中的事件循环如何的问题asyncio模块管理未完成的任务 考虑以下代码 import asyncio asyncio coroutine def a for i in range 0 3 print a str i
  • JavaFX 中的 HTML 编辑选项

    我希望将基于 Swing 的应用程序中使用的 HTML 编辑器替换为基于 JavaFX 的编辑器 据我所知 有两种可能的选择 1 使用JavaFX HTML 编辑器 虽然这对于功能来说非常好does有 它仍然缺乏其他几个功能 例如插入图像
  • 移动应用程序的 OAuth2 流程

    我们有一个预先存在的移动应用程序 用户将注册他的用户名和密码 目前我们有一个基于自定义令牌的身份验证 我们希望在不影响用户体验的情况下切换到 OAuth2 看起来资源所有者密码凭证对我们来说是最接近的流程 但是有很多建议反对使用它们 任何其
  • 使用斯坦福 NLP 检测语言

    我想知道是否可以使用Stanford CoreNLP检测句子是用哪种语言写的 如果是这样 这些算法的精确度如何 几乎可以肯定的是 斯坦福 COreNLP 目前还没有语言识别功能 几乎 因为不存在更难证明 编辑 尽管如此 以下是间接证据 也没
  • If 语句与函数指针

    目标是根据复选框是否打开或关闭来更改事件循环中的行为 我能想到的最简单的方法就是每次运行循环时测试复选框状态 if statement void action void someLoop if checkboxTrue action oth
  • AngularJS $http 从失败的 CORS 请求返回状态代码 0

    好吧 我已经查遍了这个 基本上我们使用的是跨域请求的 http 请求 我们的服务器允许该域 当请求返回 200 时 一切正常 然而 每当我们的服务器返回错误 500 401 无论什么时候 Angular 都会认为这是 CORS 问题 我使用
  • 客户端和服务器端编程有什么区别?

    我有这个代码 为什么这不会将 bar 写入我的文本文件 而是警告 42 注意 这个问题的早期修订明确涉及服务器上的 PHP 和客户端上的 JavaScript 问题的本质和解决方案是相同的any当一种语言在客户端上运行而另一种语言在服务器上
  • 访问修饰符 - 目的是什么?

    一般来说 我对编程比较陌生 我想知道是否有人可以帮助我理解访问修饰符的目的 我知道他们为类和变量等设置了不同的访问级别 但为什么要限制对这些内容的访问权限 不允许访问不同的东西有什么意义 为什么不只允许访问所有内容呢 抱歉 如果这是一个愚蠢
  • 未使用的装配参考的成本是多少?

    我想知道在 NET 解决方案中引用程序集的各种成本是多少 我对技术和组织成本都感兴趣 一些例子 未使用的程序集包含需要传送的额外字节 下载时间更长 浪费空间 未使用的程序集可能包含可利用的安全漏洞 未使用的程序集可能会产生额外的启动成本 未
  • 使用 Selenium 和 Python 查找存在 data-tb-test-id 属性而不是 id 的元素

    我正在尝试使用 Selenium 查找元素 但没有找到 请遵循 HTML 代码 div style font size 12px font weight normal color 000000 display inline block pa
  • 如何使 D3 强制布局中的标签和节点可单击以导航到 URL?

    我正在使用 D3 使用基于力的布局 并且想知道当我单击节点或标签时是否可能会自动转到存储在该节点 标签中的 url 如果是的话 我怎样才能实现这一目标 这就是图表的实际外观 标签指示 URL 本身 我正在分享我用来生成 D3 图的 Java