How join() Diffs

How join() Diffs Two Arrays of Data

With a key function, data() runs bindKey in two linear passes: it maps each existing element's key to the element (a repeated key sends it to exit); then each datum whose key is in the map becomes update and is removed, and each missing key an enter placeholder. Leftovers exit. join() ends with order():

Watching a keyed join sort items into enter, update and exitHTMLLive
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
<ul><li>harbor</li><li>deepweb</li><li>saffron</li></ul>
<script>
  d3.selectAll('li').datum(function () { return this.textContent; });   // key the old elements
  const log = (label, sel) => console.log(label, sel.data().join(' ') || '(none)');
  d3.select('ul').selectAll('li').data(['saffron', 'summit', 'harbor', 'harbor'], d => d)
    .join(enter => { log('enter: ', enter); return enter.append('li').text(d => d); },
      update => { log('update:', update); return update; },
      exit => { log('exit:  ', exit); return exit.remove(); });
  console.log('DOM:   ', d3.selectAll('li').nodes().map(n => n.textContent).join(' '));
</script>

The second harbor finds its key taken and enters. Without a key, bindIndex pairs by position, so reordered data rewrites attributes instead of moving elements.