如何在d3js中为文本元素添加href并使用缩放功能

huangapple go评论79阅读模式
英文:

How to give href to text element in d3js with zoom function

问题

我有一个带有缩放功能的d3.js图表。图表上有一些标签,我希望这些标签可以像href链接一样可点击。似乎href与缩放功能发生了冲突。只有href或缩放中的一个有效。两者不能一起工作。只有当我注释掉缩放代码时,href才有效。只有当我注释掉href代码时,缩放才有效。如何使href和缩放同时有效?

var margin = {top: 10, right: 10, bottom: 20, left: 40},
    width = 450 - margin.left - margin.right,
    height = 380 - margin.top - margin.bottom;

let data = [
    [95,'0.7142914222463218','#0000FF','(535/535)',0,"www.a.net"],
    [97,'1.5753396297416553','#00FF00','(267/267)',0,"www.b.net"],
    [97,'3.965827978600963','#808080','(14/14)',0,"www.c.net"],
    [100,'5.340348252548648','#FFAFAF','(7/8)',0,"www.d.net"],
    [100,'5.699038509168024','#FFAFAF','(1/8)',0,"www.e.net"],
    [77,'19.506535109626878','#FF0000','(5/5)',0,"www.f.net"],
    [77,'19.506535109626878','#FF0000','(2/2)',42,"www.g.net"]
];

var SVG = d3.select("body")
  .append("svg")
    .attr("xmlns", "http://www.w3.org/2000/svg")
    .attr("xlink", "http://www.w3.org/1999/xlink")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    
   SVG.append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");
   
  // Add X axis
   var x = d3.scaleLinear()
    .domain([1, 120])
    .range([ 0, width ]);
  var xAxis = SVG.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x));
   
  // Add Y axis
   var y = d3.scaleLinear()
    .domain([0, 39])
    .range([ height, 0 ]);
  var yAxis = SVG.append("g")
    .call(d3.axisLeft(y));
   
  // Add a clipPath: everything out of this area won't be drawn.
   var clip = SVG.append("defs").append("clipPath")
      .attr("id", "clip")
      .append("rect")
      .attr("width", width )
      .attr("height", height )
      .attr("x", 0)
      .attr("y", 0);
   
  // Create the scatter variable: where both the circles and the brush take place
  var scatter = SVG.append('g')
    .attr("clip-path", "url(#clip)")
    
  scatter
    .selectAll("circle")
    .data(data)
    .enter()
    .append("circle")
      .attr("cx", function (d) { return x(d[0]); } )
      .attr("cy", function (d) { return y(d[1]); } )
      .attr("r", 5)
      .style("fill", function (d) { return "black"; } )
      .style("opacity", 0.5);

   scatter.selectAll("a")
  .data(data)
  .enter().append("a")
  //.attr({"xlink:href": "#"})
  .on("click", function() { window.open("https://google.com"); })
    .append("text")
  .attr("x", (d) => x(d[0])+d[4])
  .attr("y", (d) => y(d[1]))
  .text((d) => d[3])
  .style("fill", (d) => d[2]);

  // Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
  var zoom = d3.zoom()
      .scaleExtent([.5, 1000])  // This control how much you can unzoom (x0.5) and zoom (x20)
      .extent([[0, 0], [width, height]])
      .on("zoom", updateChart);

  // This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
  SVG.append("rect")
      .attr("width", width)
      .attr("height", height)
      .style("fill", "none")
      .style("pointer-events", "all")
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
      .call(zoom);
  
  // now the user can zoom and it will trigger the function called updateChart

  // A function that updates the chart when the user zoom and thus new boundaries are available
  function updateChart() {

    // recover the new scale
    var newX = d3.event.transform.rescaleX(x);
    var newY = d3.event.transform.rescaleY(y);

    // update axes with these new boundaries
    xAxis.call(d3.axisBottom(newX))
    yAxis.call(d3.axisLeft(newY))

    // update circle position
    scatter
      .selectAll("circle")
      .attr('cx', function(d) {return newX(d[0])})
      .attr('cy', function(d) {return newY(d[1])});
    
    scatter.selectAll("text")
    .attr("x", (d) => newX(d[0])+d[4])
    .attr("y", (d) => newY(d[1]))
  }
//})
<head>
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
</head>

<body></body>
英文:

I have a d3 js chart with zoom function. There are some labels on the chart. I want those labels to be clickable like href links. It looks like href is interfering with zoom function. Only either of href or zoom is working. Both are not working together. Only if I comment zoom code, href is working. And only if I comment href code, zoom is working. How to make both href and zoom working together?

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

var margin = {top: 10, right: 10, bottom: 20, left: 40},
width = 450 - margin.left - margin.right,
height = 380 - margin.top - margin.bottom;
let data = [
[95,&#39;0.7142914222463218&#39;,&#39;#0000FF&#39;,&#39;(535/535)&#39;,0,&quot;www.a.net&quot;],[97,&#39;1.5753396297416553&#39;,&#39;#00FF00&#39;,&#39;(267/267)&#39;,0,&quot;www.b.net&quot;],[97,&#39;3.965827978600963&#39;,&#39;#808080&#39;,&#39;(14/14)&#39;,0,&quot;www.c.net&quot;],[100,&#39;5.340348252548648&#39;,&#39;#FFAFAF&#39;,&#39;(7/8)&#39;,0,&quot;www.d.net&quot;],[100,&#39;5.699038509168024&#39;,&#39;#FFAFAF&#39;,&#39;(1/8)&#39;,0,&quot;www.e.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(5/5)&#39;,0,&quot;www.f.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(2/2)&#39;,42,&quot;www.g.net&quot;]]
var SVG = d3.select(&quot;body&quot;)
.append(&quot;svg&quot;)
.attr(&quot;xmlns&quot;, &quot;http://www.w3.org/2000/svg&quot;)
.attr(&quot;xlink&quot;, &quot;http://www.w3.org/1999/xlink&quot;)
.attr(&quot;width&quot;, width + margin.left + margin.right)
.attr(&quot;height&quot;, height + margin.top + margin.bottom)
SVG.append(&quot;g&quot;)
.attr(&quot;transform&quot;,
&quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);
// Add X axis
var x = d3.scaleLinear()
.domain([1, 120])
.range([ 0, width ]);
var xAxis = SVG.append(&quot;g&quot;)
.attr(&quot;transform&quot;, &quot;translate(0,&quot; + height + &quot;)&quot;)
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 39])
.range([ height, 0]);
var yAxis = SVG.append(&quot;g&quot;)
.call(d3.axisLeft(y));
// Add a clipPath: everything out of this area won&#39;t be drawn.
var clip = SVG.append(&quot;defs&quot;).append(&quot;SVG:clipPath&quot;)
.attr(&quot;id&quot;, &quot;clip&quot;)
.append(&quot;SVG:rect&quot;)
.attr(&quot;width&quot;, width )
.attr(&quot;height&quot;, height )
.attr(&quot;x&quot;, 0)
.attr(&quot;y&quot;, 0);
// Create the scatter variable: where both the circles and the brush take place
var scatter = SVG.append(&#39;g&#39;)
.attr(&quot;clip-path&quot;, &quot;url(#clip)&quot;)
scatter
.selectAll(&quot;circle&quot;)
.data(data)
.enter()
.append(&quot;circle&quot;)
.attr(&quot;cx&quot;, function (d) { return x(d[0]); } )
.attr(&quot;cy&quot;, function (d) { return y(d[1]); } )
.attr(&quot;r&quot;, 5)
.style(&quot;fill&quot;, function (d) { &quot;black&quot;; } )
.style(&quot;opacity&quot;, 0.5);
scatter.selectAll(&quot;a&quot;)
.data(data)
.enter().append(&quot;a&quot;)
//.attr({&quot;xlink:href&quot;: &quot;#&quot;})
.on(&quot;click&quot;, function() { window.open(&quot;https://google.com&quot;); })
.append(&quot;text&quot;)
.attr(&quot;x&quot;, (d) =&gt; x(d[0])+d[4])
.attr(&quot;y&quot;, (d) =&gt; y(d[1]))
.text((d) =&gt; d[3])
.style(&quot;fill&quot;, (d) =&gt; d[2]);
// Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
var zoom = d3.zoom()
.scaleExtent([.5, 1000])  // This control how much you can unzoom (x0.5) and zoom (x20)
.extent([[0, 0], [width, height]])
.on(&quot;zoom&quot;, updateChart);
// This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
SVG.append(&quot;rect&quot;)
.attr(&quot;width&quot;, width)
.attr(&quot;height&quot;, height)
.style(&quot;fill&quot;, &quot;none&quot;)
.style(&quot;pointer-events&quot;, &quot;all&quot;)
.attr(&#39;transform&#39;, &#39;translate(&#39; + margin.left + &#39;,&#39; + margin.top + &#39;)&#39;)
.call(zoom);
// now the user can zoom and it will trigger the function called updateChart
// A function that updates the chart when the user zoom and thus new boundaries are available
function updateChart() {
// recover the new scale
var newX = d3.event.transform.rescaleX(x);
var newY = d3.event.transform.rescaleY(y);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX))
yAxis.call(d3.axisLeft(newY))
// update circle position
scatter
.selectAll(&quot;circle&quot;)
.attr(&#39;cx&#39;, function(d) {return newX(d[0])})
.attr(&#39;cy&#39;, function(d) {return newY(d[1])});
scatter.selectAll(&quot;text&quot;)
.attr(&quot;x&quot;, (d) =&gt; newX(d[0])+d[4])
.attr(&quot;y&quot;, (d) =&gt; newY(d[1]))
}
//})

<!-- language: lang-html -->

&lt;head&gt;
&lt;!-- Load d3.js --&gt;
&lt;script src=&quot;https://d3js.org/d3.v4.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;&lt;/body&gt;

<!-- end snippet -->

答案1

得分: 1

以下是您要翻译的内容:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

    var margin = {top: 10, right: 10, bottom: 20, left: 40},
        width = 450 - margin.left - margin.right,
        height = 380 - margin.top - margin.bottom;

    let data = [
    	[95,'0.7142914222463218','#0000FF','(535/535)',0,"www.a.net"],[97,'1.5753396297416553','#00FF00','(267/267)',0,"www.b.net"],[97,'3.965827978600963','#808080','(14/14)',0,"www.c.net"],[100,'5.340348252548648','#FFAFAF','(7/8)',0,"www.d.net"],[100,'5.699038509168024','#FFAFAF','(1/8)',0,"www.e.net"],[77,'19.506535109626878','#FF0000','(5/5)',0,"www.f.net"],[77,'19.506535109626878','#FF0000','(2/2)',42,"www.g.net"]]

    var SVG = d3.select("body")
      .append("svg")
        .attr("xmlns", "http://www.w3.org/2000/svg")
        .attr("xlink", "http://www.w3.org/1999/xlink")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        
       SVG.append("g")
        .attr("transform",
              "translate(" + margin.left + "," + margin.top + ")");
       
      // Add X axis
       var x = d3.scaleLinear()
        .domain([1, 120])
        .range([ 0, width ]);
      var xAxis = SVG.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x));
       
      // Add Y axis
       var y = d3.scaleLinear()
        .domain([0, 39])
        .range([ height, 0 ]);
      var yAxis = SVG.append("g")
        .call(d3.axisLeft(y));
       
      // Add a clipPath: everything out of this area won't be drawn.
       var clip = SVG.append("defs").append("clipPath")
          .attr("id", "clip")
          .append("rect")
          .attr("width", width )
          .attr("height", height )
          .attr("x", 0)
          .attr("y", 0);
       
      // Create the scatter variable: where both the circles and the brush take place
      var scatter = SVG.append('g')
        .attr("clip-path", "url(#clip)")
        
      scatter
        .selectAll("circle")
        .data(data)
        .enter()
        .append("circle")
          .attr("cx", function (d) { return x(d[0]); } )
          .attr("cy", function (d) { return y(d[1]); } )
          .attr("r", 5)
          .style("fill", function (d) { "black"; } )
          .style("opacity", 0.5);

       scatter.selectAll(".textg")
      .data(data)
      .enter().append("g")
      .attr('class','textg')
      .style('cursor','pointer')
      //.attr({"xlink:href": "#"})
      .on("click", function() { window.open("https://google.com"); })
      
        .append("text")
    //    .attr({x: (d) => x(d[0])+d[4], y: (d) => y(d[1])})
      .attr("x", (d) => x(d[0])+d[4])
      .attr("y", (d) => y(d[1]))
      .text((d) => d[3])
      .style("fill", (d) => d[2]);

      // Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
      var zoom = d3.zoom()
          .scaleExtent([.5, 1000])  // This control how much you can unzoom (x0.5) and zoom (x20)
          .extent([[0, 0], [width, height]])
          .on("zoom", updateChart);

      // This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
      SVG.append("rect")
          .attr("width", width)
          .attr("height", height)
          .style("fill", "none")
          .style("pointer-events", "all")
          .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
          .call(zoom);
          scatter.raise()
      
      // now the user can zoom and it will trigger the function called updateChart

      // A function that updates the chart when the user zoom and thus new boundaries are available
      function updateChart() {

        // recover the new scale
        var newX = d3.event.transform.rescaleX(x);
        var newY = d3.event.transform.rescaleY(y);

        // update axes with these new boundaries
        xAxis.call(d3.axisBottom(newX))
        yAxis.call(d3.axisLeft(newY))

        // update circle position
        scatter
          .selectAll("circle")
          .attr('cx', function(d) {return newX(d[0])})
          .attr('cy', function(d) {return newY(d[1])});
        
        scatter.selectAll("text")
        .attr("x", (d) => newX(d[0])+d[4])
        .attr("y", (d) => newY(d[1]))
        
          scatter.raise()
      }
    //})

<!-- language: lang-html -->

    <head>
    <!-- Load d3.js -->
    <script src="https://d3js.org/d3.v4.js"></script>
    </head>

    <body></body>

<!-- end snippet -->

请注意,上述内容包括HTML和JavaScript代码,已根据您的要求进行翻译。如果您需要进一步的帮助,请随时提问。

英文:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

var margin = {top: 10, right: 10, bottom: 20, left: 40},
width = 450 - margin.left - margin.right,
height = 380 - margin.top - margin.bottom;
let data = [
[95,&#39;0.7142914222463218&#39;,&#39;#0000FF&#39;,&#39;(535/535)&#39;,0,&quot;www.a.net&quot;],[97,&#39;1.5753396297416553&#39;,&#39;#00FF00&#39;,&#39;(267/267)&#39;,0,&quot;www.b.net&quot;],[97,&#39;3.965827978600963&#39;,&#39;#808080&#39;,&#39;(14/14)&#39;,0,&quot;www.c.net&quot;],[100,&#39;5.340348252548648&#39;,&#39;#FFAFAF&#39;,&#39;(7/8)&#39;,0,&quot;www.d.net&quot;],[100,&#39;5.699038509168024&#39;,&#39;#FFAFAF&#39;,&#39;(1/8)&#39;,0,&quot;www.e.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(5/5)&#39;,0,&quot;www.f.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(2/2)&#39;,42,&quot;www.g.net&quot;]]
var SVG = d3.select(&quot;body&quot;)
.append(&quot;svg&quot;)
.attr(&quot;xmlns&quot;, &quot;http://www.w3.org/2000/svg&quot;)
.attr(&quot;xlink&quot;, &quot;http://www.w3.org/1999/xlink&quot;)
.attr(&quot;width&quot;, width + margin.left + margin.right)
.attr(&quot;height&quot;, height + margin.top + margin.bottom)
SVG.append(&quot;g&quot;)
.attr(&quot;transform&quot;,
&quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);
// Add X axis
var x = d3.scaleLinear()
.domain([1, 120])
.range([ 0, width ]);
var xAxis = SVG.append(&quot;g&quot;)
.attr(&quot;transform&quot;, &quot;translate(0,&quot; + height + &quot;)&quot;)
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 39])
.range([ height, 0]);
var yAxis = SVG.append(&quot;g&quot;)
.call(d3.axisLeft(y));
// Add a clipPath: everything out of this area won&#39;t be drawn.
var clip = SVG.append(&quot;defs&quot;).append(&quot;SVG:clipPath&quot;)
.attr(&quot;id&quot;, &quot;clip&quot;)
.append(&quot;SVG:rect&quot;)
.attr(&quot;width&quot;, width )
.attr(&quot;height&quot;, height )
.attr(&quot;x&quot;, 0)
.attr(&quot;y&quot;, 0);
// Create the scatter variable: where both the circles and the brush take place
var scatter = SVG.append(&#39;g&#39;)
.attr(&quot;clip-path&quot;, &quot;url(#clip)&quot;)
scatter
.selectAll(&quot;circle&quot;)
.data(data)
.enter()
.append(&quot;circle&quot;)
.attr(&quot;cx&quot;, function (d) { return x(d[0]); } )
.attr(&quot;cy&quot;, function (d) { return y(d[1]); } )
.attr(&quot;r&quot;, 5)
.style(&quot;fill&quot;, function (d) { &quot;black&quot;; } )
.style(&quot;opacity&quot;, 0.5);
scatter.selectAll(&quot;.textg&quot;)
.data(data)
.enter().append(&quot;g&quot;)
.attr(&#39;class&#39;,&#39;textg&#39;)
.style(&#39;cursor&#39;,&#39;pointer&#39;)
//.attr({&quot;xlink:href&quot;: &quot;#&quot;})
.on(&quot;click&quot;, function() { window.open(&quot;https://google.com&quot;); })
.append(&quot;text&quot;)
//    .attr({x: (d) =&gt; x(d[0])+d[4], y: (d) =&gt; y(d[1])})
.attr(&quot;x&quot;, (d) =&gt; x(d[0])+d[4])
.attr(&quot;y&quot;, (d) =&gt; y(d[1]))
.text((d) =&gt; d[3])
.style(&quot;fill&quot;, (d) =&gt; d[2]);
// Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
var zoom = d3.zoom()
.scaleExtent([.5, 1000])  // This control how much you can unzoom (x0.5) and zoom (x20)
.extent([[0, 0], [width, height]])
.on(&quot;zoom&quot;, updateChart);
// This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
SVG.append(&quot;rect&quot;)
.attr(&quot;width&quot;, width)
.attr(&quot;height&quot;, height)
.style(&quot;fill&quot;, &quot;none&quot;)
.style(&quot;pointer-events&quot;, &quot;all&quot;)
.attr(&#39;transform&#39;, &#39;translate(&#39; + margin.left + &#39;,&#39; + margin.top + &#39;)&#39;)
.call(zoom);
scatter.raise()
// now the user can zoom and it will trigger the function called updateChart
// A function that updates the chart when the user zoom and thus new boundaries are available
function updateChart() {
// recover the new scale
var newX = d3.event.transform.rescaleX(x);
var newY = d3.event.transform.rescaleY(y);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX))
yAxis.call(d3.axisLeft(newY))
// update circle position
scatter
.selectAll(&quot;circle&quot;)
.attr(&#39;cx&#39;, function(d) {return newX(d[0])})
.attr(&#39;cy&#39;, function(d) {return newY(d[1])});
scatter.selectAll(&quot;text&quot;)
.attr(&quot;x&quot;, (d) =&gt; newX(d[0])+d[4])
.attr(&quot;y&quot;, (d) =&gt; newY(d[1]))
scatter.raise()
}
//})

<!-- language: lang-html -->

&lt;head&gt;
&lt;!-- Load d3.js --&gt;
&lt;script src=&quot;https://d3js.org/d3.v4.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;&lt;/body&gt;

<!-- end snippet -->

Hello, i think your problem is that d3.zoom is putting e rect to capture mouse events.

So one solution is to put the &lt;g/&gt; on top of the rect so you can click your elements.

Luckily d3 has an easy way to bring an element to the top:
scatter.raise()

Click doesn't work on sniper because you can't open a new window on snippet sandbox, however you can see the cursor change on text over

huangapple
  • 本文由 发表于 2023年8月5日 00:08:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76837583.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定