【问题标题】:D3 simple drop down menu on clickD3点击时的简单下拉菜单
【发布时间】:2017-03-08 20:47:37
【问题描述】:

我在任何地方都找不到直截了当的答案。如何在单击 svg 圆圈时显示下拉菜单?在单击形状之前,我不希望它显示出来。有人能帮助我吗?

【问题讨论】:

    标签: d3.js


    【解决方案1】:

    下载并包含在您的 html 中:https://github.com/patorjk/d3-context-menu

    并在点击一个圆圈时使用:

    circle.on('contextmenu', d3.contextMenu(menu)); // attach menu to element
    

    如果你想要插件的话:)

    编辑

    没有插件

    如果您不想要插件,只需创建要显示的内容列表,阻止默认行为并右键单击显示创建的列表并单击其他任何位置隐藏它。

    这是一个小提琴(不是我的):http://jsfiddle.net/thatoneguy/u2kJq/727/

    他创建的列表:

    <ul class='custom-menu'>
      <li data-action = "first">First thing</li>
      <li data-action = "second">Second thing</li>
      <li data-action = "third">Third thing</li>
    </ul>
    

    这将在右键单击时显示。现在要使用 D3 实现这一点,您可以像上面的小提琴一样使用event.preventDefault();,您必须使用d3.event.preventDefault();。您还必须定位菜单,因此将 css 顶部和左侧更改为鼠标位置:

     css({
          top: d3.event.pageY + "px",
          left: d3.event.pageX + "px"
        });
    

    这是我所描述的一个简单实现:

    var w = 500;
    var h = 50;
    
    var dataset = [5, 10, 15, 20, 25];
    
    var svg = d3.select("body")
      .append("svg")
      .attr("width", w)
      .attr("height", h);
    
    var circles = svg.selectAll("circle")
      .data(dataset)
      .enter()
      .append("circle");
    
    circles.attr("cx", function(d, i) {
        return (i * 50) + 25;
      })
      .attr("cy", h / 2)
      .attr("r", function(d) {
        return d;
      })
      .on("contextmenu", function(event) {
    
        // Avoid the real one
       d3.event.preventDefault();
    
        // Show contextmenu
        $(".custom-menu").finish().toggle(100).
    
        // In the right position (the mouse)
        css({
          top: d3.event.pageY + "px",
          left: d3.event.pageX + "px"
        });
      });;
    
    
    
    
    //$(document).bind
    
    
    // If the document is clicked somewhere
    $(document).bind("mousedown", function(e) {
    
      // If the clicked element is not the menu
      if (!$(e.target).parents(".custom-menu").length > 0) {
    
        // Hide it
        $(".custom-menu").hide(100);
      }
    });
    
    
    // If the menu element is clicked
    $(".custom-menu li").click(function() {
    
      // This is the triggered action name
      switch ($(this).attr("data-action")) {
    
        // A case for each action. Your actions here
        case "first":
          alert("first");
          break;
        case "second":
          alert("second");
          break;
        case "third":
          alert("third");
          break;
      }
    
      // Hide it AFTER the action was triggered
      $(".custom-menu").hide(100);
    });
    .custom-menu {
        display: none;
        z-index: 1000;
        position: absolute;
        overflow: hidden;
        border: 1px solid #CCC;
        white-space: nowrap;
        font-family: sans-serif;
        background: #FFF;
        color: #333;
        border-radius: 5px;
    }
    
    .custom-menu li {
        padding: 8px 12px;
        cursor: pointer;
    }
    
    .custom-menu li:hover {
        background-color: #DEF;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <body>
    <ul class='custom-menu'>
      <li data-action = "first">First thing</li>
      <li data-action = "second">Second thing</li>
      <li data-action = "third">Third thing</li>
    </ul>
    </body>

    如果您想在单击其中一个菜单项时执行某些操作,可以在 html 中使用 onclick,如下所示:

    <li onclick='doSomething()' data-action = "third">Third thing</li>
    

    或者你可以像这样给单个项目一个id:

    <li id='listItemOne' data-action = "third">Third thing</li>
    

    然后添加事件监听器:

    document.getElementById('listItemOne').addEventListener('click', doSomething)
    

    【讨论】:

    • 好的,我会调查一下,谢谢,有没有办法在 d3 中没有插件的情况下做到这一点?还是插件更常用于这种事情?再次感谢@thisOneGuy 的回复
    • @thisOneGuy,很好的答案。
    • @thisOneGuy 非常感谢,这真的让我朝着正确的方向前进,真的很感激!
    【解决方案2】:

    @thisOneGuy 给出了一个很好的答案,但是由于我已经对其进行了编码,所以这里有一个使用svg 构建上下文菜单的解决方案:

    <!DOCTYPE html>
    <html>
    
    <head>
      <script data-require="d3@3.4.6" data-semver="3.4.6" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.6/d3.min.js"></script>
      <style>
        svg {
          font: 12px sans-serif;
        }
      </style>
    </head>
    
    <body>
    
      <script>
        var menu = [{
          title: 'A really, really long item',
          action: function(elem, d, i) {
            console.log('Item #1 clicked!');
            console.log('The data for this circle is: ' + d);
          }
        }, {
          title: 'Item #2',
          action: function(elem, d, i) {
            console.log('You have clicked the second item!');
            console.log('The data for this circle is: ' + d);
          }
        }]
    
        var data = [1, 2, 3];
    
        var g = d3.select('body').append('svg')
          .on('click', function(){
            m.style('display', 'none');
          })
          .attr('width', 500)
          .attr('height', 400)
          .append('g');
    
        g.selectAll('circles')
          .data(data)
          .enter()
          .append('circle')
          .attr('r', 30)
          .attr('fill', 'steelblue')
          .attr('cx', function(d) {
            return 100;
          })
          .attr('cy', function(d) {
            return d * 100;
          })
          .on('contextmenu', function(d) {
            var coors = d3.mouse(this);
    
            m.attr('transform', 'translate(' + coors[0] + ',' + coors[1] + ')');
            m.style('display', 'block');
            m.datum(d);
    
            d3.event.preventDefault();
          });
        
        /* build context menu */
        var m = g.append("g")
          m.style('display', 'none');
    
        var r = m.append('rect')
          .attr('height', menu.length * 25)
          .style('fill', "#eee");
    
        var t = m.selectAll('menu_item')
          .data(menu)
          .enter()
          .append('g')
          .attr('transform', function(d, i) {
            return 'translate(' + 10 + ',' + ((i + 1) * 20) + ')';
          })
          .on('mouseover', function(d){
            d3.select(this).style('fill', 'steelblue');
          })
          .on('mouseout', function(d){
            d3.select(this).style('fill', 'black');
          })
          .on('click', function(d,i){
            d.action(d, d3.select(this.parentNode).datum(), i);
          })
          .append('text')
          .text(function(d) {
            return d.title;
          });
        
        var w = 0;
        t.each(function(d){
          var l = this.getComputedTextLength();
          if (l > w) w = l;
        })
        r.attr('width', w + 20);  
      
      </script>
    </body>
    
    </html>

    【讨论】:

    • 道歉 :L 我知道当你花时间自己编写答案时有人回答同样的问题是多么烦人 :)
    • @thisOneGuy,不用担心。我实际上认为你的第一个答案是最好的。查看该插件的源代码,它做得非常好。
    • 是的,这很好,但是 OP 要求没有插件,因为您可能已经注意到两者都实现了:)
    • @Mark 非常感谢,这非常清晰易懂,非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    • 2015-03-27
    • 1970-01-01
    • 2021-11-17
    相关资源
    最近更新 更多