Search code examples
animationd3.jstextautomatic-ref-countingonmouseover

Transition selected textpath from multiple textpaths on mouseover in D3.js v5


I am trying to transition only the textpath on which I am mousehovering but both textpaths in the example below get transitioned. Is there a way to only transition the one when hovering?

For the code below I modified this example and I am using version 5 of D3.js.

Here is the script:

<script>
    //Create the SVG
    var svg = d3.select("body").append("svg")
                .attr("width", 900)
                .attr("height", 900);

    class graph {

        constructor(opts){
            this.x = opts.x;
            this.y = opts.y;

            this.container = svg.append('g')
                .attr('class', 'country-wrapper')
                .attr('transform', 'translate(' + this.x + ',' + this.y + ')')
                .on('mouseover', this.handleMouseOver);
            this.background = this.container.append('g')
                .attr('class', 'background-viz')
            this.appendText();
        }
        appendText(){
            var d = "M0,300 A200,200 0 0,1 400,300";
            console.log(d);
            var path = this.background.append("path")
                .attr("id", "wavy")
                .attr("d", d)
                .style("fill", "none")
                .style("stroke", "#AAAAAA")
                .style("stroke-dasharray", "5,5");


            var textArc = this.background.append("text")
                .style("text-anchor","middle")
              .append("textPath")
                .attr("xlink:href", "#wavy")
                .attr("startOffset", "50%") 
                .text("placeholder for text here");
        }


        handleMouseOver(){
            var d = "M75,300 A125,125 0 0,1 325,300";
            console.log('new ', d);
            d3.select(this).select('.background-viz').selectAll('path')
                .transition()
                .attr("d", "M75,300 A125,125 0 0,1 325,300");
        }
    }

    for (var i = 0; i < 2; i++){
        new graph({
            x: i  * 900/2,
            y: 0
        });
    }
</script>

Solution

  • The issue is with the following line:

    textPath.attr("xlink:href", "#wavy")

    as both path have the same IDs and as you hover onto one, you see both the textPaths transitioning. You have to differentiate the IDs based on some value. Best solution would be to pass an ID and use it:

    Here's how:

    1. Pass ID while creating the paths, texts i.e. to every instance

      new graph({
          x: i  * 900/2,
          y: 0,
          id: i
      });
      
    2. Use it/apply it to the paths and textPaths:

      var path = this.background.append("path")
         .attr("id", "wavy-"+this.id)
       ....
      
       .append("textPath")
       .attr("xlink:href", "#wavy-"+this.id)
      

    Using the above changes, here's a code snippet:

        //Create the SVG
        var svg = d3.select("body").append("svg")
                    .attr("width", 900)
                    .attr("height", 900);
                    
        class graph {
    
            constructor(opts){
                this.x = opts.x;
                this.y = opts.y;
                this.id = opts.id;
    
                this.container = svg.append('g')
                    .attr('class', 'country-wrapper')
                    .attr('transform', 'translate(' + this.x + ',' + this.y + ')')
                    .on('mouseover', this.handleMouseOver);
                this.background = this.container.append('g')
                    .attr('class', 'background-viz')
                this.appendText();
            }
            appendText(){
                var d = "M0,300 A200,200 0 0,1 400,300";
                var path = this.background.append("path")
                    .attr("id", "wavy-"+this.id)
                    .attr("d", d)
                    .style("fill", "none")
                    .style("stroke", "#AAAAAA")
                    .style("stroke-dasharray", "5,5");
    
    
                var textArc = this.background.append("text")
                    .style("text-anchor","middle")
                  .append("textPath")
                    .attr("xlink:href", "#wavy-"+this.id)
                    .attr("startOffset", "50%") 
                    .text("placeholder for text here");
            }
    
            
            handleMouseOver(){
                var d = "M75,300 A125,125 0 0,1 325,300";
                //console.log('new ', d);
                d3.select(this).select('.background-viz').selectAll('path')
                    .transition()
                    .attr("d", "M75,300 A125,125 0 0,1 325,300");
            }
        }
    
        for (var i = 0; i < 2; i++){
            new graph({
                x: i  * 900/2,
                y: 0,
                id: i
            });
        }
    <script src="https://d3js.org/d3.v5.min.js"></script>

    Hope this helps.