How to write re-usable function which access the data of the parent element and insert child elements in D3?

This is a snippet from a notebook. Here, I’m splitting a string at \n and inserting each using <tspan> within <text>.

I need to use this functionality in more than one places. What is a good way that I can write a function and use it in many places? I tried using selection.call, but I didn’t find a good way to access the data from the parent <text>.

  const text = `A world of dew,
And within every dewdrop
A world of struggle.
—Kobayashi Issa`;

  const lines = text.split("\n");
  const textContentHeight = (lines.length - 1) * lineHeight * fontSize;

  svg
    .append("text")
    // 👋 Is it possible to create re-usable function to do this function using selection.call(),
    // selection.each()?
    // .data(text)
    // .call(multilineText, {fontFamily, fontFamily, textAnchor})
    .attr("font-family", fontFamily)
    .attr("font-size", fontSize)
    .attr("dominant-baseline", "middle")
    .attr("text-anchor", textAnchor)
    .selectAll("tspan")
    .data(lines)
    .join("tspan")
    .text((d) => d)
    .attr("x", anchor.x)
    .attr(
      "y",
      (d, i) => anchor.y + i * lineHeight * fontSize - textContentHeight / 2
    );
1 Like

Figured!

I was doing the selection.data() wrong. It needed an array. Also, within called function, need to iterate over selection using selection.each().

Here is the updated snippet:

svg
    .selectAll("text")
    .data([text])
    .join("text")
    .attr("x", anchor.x)
    .attr("y", anchor.y)
    .call(multilineText, {
      fontFamily,
      fontSize,
      lineHeight,
      textAnchor,
      dominantBaseline
    });

function multilineText(
  el,
  {
    fontFamily,
    fontSize = 10,
    lineHeight = 1.45,
    textAnchor = "start",
    dominantBaseline = "auto"
  } = {}
) {
  el.each(function (text) {
    const lines = text.split("\n");
    const textContentHeight = (lines.length - 1) * lineHeight * fontSize;

    const el = d3.select(this);
    const anchor = {
      x: +el.attr("x"),
      y: +el.attr("y")
    };

    const dy =
      dominantBaseline === "middle"
        ? -textContentHeight / 2
        : dominantBaseline === "hanging"
        ? -textContentHeight
        : 0;

    el.attr("font-family", fontFamily)
      .attr("font-size", fontSize)
      .attr("dominant-baseline", dominantBaseline)
      .attr("text-anchor", textAnchor)
      .selectAll("tspan")
      .data(lines)
      .join("tspan")
      .text((d) => d)
      .attr("x", anchor.x)
      .attr("y", (d, i) => anchor.y + i * lineHeight * fontSize + dy);
  });
}
5 Likes