title: D3 linkText appends instead updating existing value
body: <p>Recently I migrated my graph to v6 where I replaced the <code>enter()</code>, <code>remove()</code>, <code>exit()</code> pattern with <code>join()</code>. Unfortunately, I can´t change the link text any longer. Usually, the text should switch between &quot;blue&quot; and &quot;green&quot; as soon as a link was clicked.</p>
  <p>I know, that I append a new text object instead of updating the existing value. Which is the problem. It's visible in the browser inspector. But I can´t figure out how to adapt the code, as <code>join()</code> is not working at this point.</p>
  <p>Any hints?</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
  
  &lt;head&gt;
      &lt;meta charset="utf-8"&gt;
      &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  
      &lt;title&gt;D3 JOIN Test&lt;/title&gt;
      &lt;!-- call external d3.js framework --&gt;
      &lt;script src="https://d3js.org/d3.v6.js"&gt;&lt;/script&gt;
      &lt;!-- import multiselection framework --&gt;
      &lt;script src="https://d3js.org/d3-selection-multi.v1.js"&gt;&lt;/script&gt;
  &lt;/head&gt;
  
  &lt;style&gt;
      body {
          overflow: hidden;
          margin: 0px;
      }
  
      .canvas {
          background-color: rgb(220, 220, 220);
      }
  
      .link {
          cursor: pointer;
          stroke: rgb(0, 0, 0);
          stroke-width: 4px;
      }
  
      .link:hover {
          stroke: red;
      }
  
  &lt;/style&gt;
  
  &lt;body&gt;
  
      &lt;svg id="svg"&gt; &lt;/svg&gt;
  
      &lt;script&gt;
          var graph = {
              "nodes": [
                  {
                      "id": 1,
                  },
                  {
                      "id": 2,
                  },
                  {
                      "id": 3,
                  }
              ],
              "links": [
                  {
                      "source": 1,
                      "target": 2,
                      "type": "blue"
                  },
                  {
                      "source": 2,
                      "target": 3,
                      "type": "blue"
                  },
                  {
                      "source": 3,
                      "target": 1,
                      "type": "blue"
                  }
              ]
          }
  
  
          // declare initial variables
          var svg = d3.select("svg")
          width = window.innerWidth
          height = window.innerHeight
  
          // define cavnas area to draw everything
          svg = d3.select("svg")
              .attr("class", "canvas")
              .attr("width", width)
              .attr("height", height)
              .call(d3.zoom().on("zoom", function (event) {
                  svg.attr("transform", event.transform)
              }))
              .append("g")
  
          // remove zoom on dblclick listener
          d3.select("svg").on("dblclick.zoom", null)
  
          var linksContainer = svg.append("g").attr("class", "linksContainer")
          var nodesContainer = svg.append("g").attr("class", "nodesContainer")
  
          // iniital force simulation
          var simulation = d3.forceSimulation()
              .force("link", d3.forceLink().id(function (d) {
                  return d.id;
              }).distance(200))
              .force("charge", d3.forceManyBody().strength(-100))
              .force("center", d3.forceCenter(width / 2, height / 2))
              .force("attraceForce", d3.forceManyBody().strength(70));
  
          initialze()
  
  
          function initialze() {
              links = linksContainer.selectAll(".link")
                  .data(graph.links)
                  .join("line")
                  .attr("class", "link")
                  .on("click", click)
  
              linkPaths = linksContainer.selectAll(".linkPath")
                  .data(graph.links)
                  .join("path")
                  .style("pointer-events", "none")
                  .attrs({
                      "class": "linkPath",
                      "fill-opacity": 1,
                      "stroke-opacity": 1,
                      "id": function (d, i) { return "linkPath" + i }
                  })
                  .style("display", "block")
  
              linkLabels = linksContainer.selectAll(".linkLabel")
                  .data(graph.links)
                  .join("text")
                  .style("pointer-events", "none")
                  .attrs({
                      "class": "linkLabel",
                      "id": function (d, i) { return "linkLabel" + i },
                      "font-size": 16,
                      "fill": "black"
                  })
                  .style("display", "block")
  
              linkLabels
                  .append("textPath")
                  .attr('xlink:href', function (d, i) { return '#linkPath' + i })
                  .style("text-anchor", "middle")
                  .style("pointer-events", "none")
                  .attr("startOffset", "50%")
                  .text(function (d) { return d.type })
  
              nodes = nodesContainer.selectAll(".node")
                  .data(graph.nodes, d =&gt; d.id)
                  .join("circle")
                  .attr("class", "node")
                  .attr("r", 30)
                  .attr("fill", "whitesmoke")
                  .attr("stroke", "white")
                  .call(d3.drag()
                      .on("start", dragStarted)
                      .on("drag", dragged)
                      .on("end", dragEnded)
                  )
  
              simulation
                  .nodes(graph.nodes)
                  .on("tick", ticked);
  
              simulation
                  .force("link")
                  .links(graph.links)
          }
  
  
          function mouseEnter(event, d) {
              d3.select(this).style("fill", "lightblue")
          }
  
  
          function mouseLeave(event, d) {
              d3.select(this).style("fill", "whitesmoke")
          }
  
  
          function click(event, d) {
              if (d.type == "blue") {
                  d.type = "green"
              } else if (d.type == "green") {
                  d.tyoe = "blue"
              }
  
              initialze()
          }
  
          function close() {
              contextMenuLink.classList.remove("active")
          }
  
          function ticked() {
              links
                  .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;
                  });
  
              nodes
                  .attr("transform", function (d) {
                      return "translate(" + d.x + ", " + d.y + ")";
                  });
  
              linkPaths.attr('d', function (d) {
                  return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
              });
  
              linkLabels.attr('transform', function (d) {
                  if (d.target.x &lt; d.source.x) {
                      var bbox = this.getBBox();
  
                      rx = bbox.x + bbox.width / 2;
                      ry = bbox.y + bbox.height / 2;
                      return 'rotate(180 ' + rx + ' ' + ry + ')';
                  }
                  else {
                      return 'rotate(0)';
                  }
              });
          }
  
  
          function dragStarted(event, d) {
              if (!event.active) simulation.alphaTarget(0.3).restart();
              d.fx = d.x;
              d.fy = d.y;
          }
  
  
          function dragged(event, d) {
              d.fx = event.x;
              d.fy = event.y;
          }
  
  
          function dragEnded(event, d) {
              if (!event.active) simulation.alphaTarget(0);
              d.fx = undefined;
              d.fy = undefined;
          }
  
      &lt;/script&gt;
  &lt;/body&gt;
  
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>