D3 x and y coordinates applied to last appened element instead of the group

I have a force directed graph where each node is a group and contains a foreignObject and an img inside it. x and y coordiantes are applied to the img tag but I would like to have them on the foreignObject or g. I am not sure how this can be achieved.

I tried setting x and y coordinates to the foreignObject but they are always returned as NaN.

const plot = (data) => {
  data.nodes = data.nodes.map((d, index) => {
    d['id'] = index;
    return d;
  });
  const margin = {
    top: 20,
    right: 20,
    bottom: 10,
    left: 100
  };
  const width = Math.max((((window.innerWidth / 100) * 80) - margin.right - margin.left), 700);
  const height = ((window.innerHeight / 100) * 80) - margin.bottom - margin.top;
  const svg = d3.select('svg')
    .attr('width', width + margin.left + margin.right + 100)
    .attr('height', height + margin.top + margin.bottom + 100)
    .append('g')
    .attr('transform',
      `translate(${margin.left}, ${margin.top})`);

  const simulation = d3.forceSimulation()
    .force('link', d3.forceLink().id(function (d) { return d.id; }).distance(100).strength(1))
    .force('charge', d3.forceManyBody())
    .force('center', d3.forceCenter(width / 2, height / 2));

  const dragstarted = d => {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  const dragged = d => {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  const dragended = d => {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }

  const ticked = () => {
    link
      .attr("x1", d => d.source.x)
      .attr("y1", d => d.source.y)
      .attr("x2", d => d.target.x)
      .attr("y2", d => d.target.y);

    node
      .attr("x", function (d) { return d.x = Math.max(5, Math.min(width - 5, d.x)); })
      .attr("y", function (d) { return d.y = Math.max(5, Math.min(height - 5, d.y)); });
  }

  const link = svg.append('g')
    .attr('class', 'links')
    .selectAll('line')
    .data(data.links)
    .enter().append('line')
    .attr('stroke-width', function (d) { return Math.sqrt(d.value); });

  const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.node-group')
    .data(data.nodes)
    .enter()
    .append('g')
    .append('foreignObject')
    .attr('class', 'node-group')
    .attr('width', '10')
    .attr('height', '10')
    .insert('xhtml:img')
    .attr('src', 'flags/blank.png')
    .attr('class', d => `flag flag-${d.code}`)
    .call(d3.drag()
      .on('start', dragstarted)
      .on('drag', dragged)
      .on('end', dragended));

  node.append("title")
    .text(function (d) { return d.country; })
    .exit();

  simulation
    .nodes(data.nodes)
    .on("tick", ticked);

  simulation.force("link")
    .links(data.links);

}

Answers:

Answer

If you look at your node selection...

const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.node-group')
    .data(data.nodes)
    .enter()
    .append('g')
    .append('foreignObject')
    .attr('class', 'node-group')
    .attr('width', '10')
    .attr('height', '10')
    .insert('xhtml:img')
    .attr('src', 'flags/blank.png')
    .attr('class', d => `flag flag-${d.code}`)
    .call(d3.drag()
        .on('start', dragstarted)
        .on('drag', dragged)
        .on('end', dragended));

... you'll see that it is a selection with the images. Since you want to apply the x and y properties to the foreignObject, just break it:

const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.node-group')
    .data(data.nodes)
    .enter()
    .append('g')
    .append('foreignObject')
    .attr('class', 'node-group')
    .attr('width', '10')
    .attr('height', '10');

node.insert('xhtml:img')
    .attr('src', 'flags/blank.png')
    .attr('class', d => `flag flag-${d.code}`)
    .call(d3.drag()
        .on('start', dragstarted)
        .on('drag', dragged)
        .on('end', dragended));

That way, node is a selection with the foreignObjects.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.