D3.js Перемещение элементов в структуре svg

#javascript #svg #dom #d3.js

Вопрос:

Я пишу вам, потому что я борюсь с D3.js (D3-выделение) для перемещения позиций существующих элементов в SVG.

Я видел много примеров создания новых элементов, но здесь мои элементы уже созданы.

У меня есть эта структура в моем svg :

 <g id='maingroup' class='main'>
  <title>titlemain</main>
  <text id='text'>textContent</text>
</g>

<g id='othergroup1' class='othergroup'>
  <title>othergroup1</title>
  <text id='text1'>textContent1</text>
  <ellipse fill="#ac08b6" cx="198.5" cy="25" rx="27" ry="18"></ellipse>
</g>

<g id='othergroup2' class='othergroup'>
  <title>othergroup2</title>
  <text id='text2'>textContent2</text>
  <ellipse fill="#23d5f6" cx="198.5" cy="25" rx="27" ry="18"></ellipse>
</g>
 

Моя цель состоит в том, чтобы переместить все многоточия в основную группу, чтобы получить :

 <g id='maingroup' class='main'>
  <title>titlemain</main>
  <text id='text'>textContent</text>
  <ellipse fill="#ac08b6" cx="198.5" cy="25" rx="27" ry="18"></ellipse>
  <ellipse fill="#23d5f6" cx="198.5" cy="25" rx="27" ry="18"></ellipse>
</g>
 

Мне удалось сделать это благодаря участию в D3.js и часть с манипуляциями с ДОМОМ.

 svg = d3.select('svg')
allellipses = svg.selectAll('ellipse').nodes()
for (ell of allellipses) {document.querySelector('.main').append(ell)}
 

Есть ли способ сделать это только с помощью D3.js ? Я хотел бы заменить document.querySelector на D3.js функция. По крайней мере, знать и понимать, как работает добавление существующих элементов.

Но, возможно, для этой операции эффективнее использовать только простые манипуляции с DOM.

Ответ №1:

функция selection.remove() удаляет узлы из DOM, возвращая выбранные из них узлы.

selection.append() может быть предоставлена функция, которая добавляет данный узел.

Таким образом, мы можем удалить узлы, использовать узлы в качестве массива данных и ввести/добавить удаляемые эллипсы:

 var ellipse = svg.selectAll("ellipse").remove();

svg.select("#maingroup")
  .selectAll(null)
  .data(ellipse.nodes())
  .enter()
  .append(d=>d);
 
 var svg = d3.select("svg");

var ellipse = svg.selectAll("ellipse").remove();

svg.select("#maingroup")
  .selectAll(null)
  .data(ellipse.nodes())
  .enter()
  .append(d=>d); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg>
<g id='maingroup' class='main'>
  <title>titlemain</main>
  <text id='text'>textContent</text>
</g>

<g id='othergroup1' class='othergroup'>
  <title>othergroup1</title>
  <text id='text1'>textContent1</text>
  <ellipse fill="#ac08b6" cx="198.5" cy="25" rx="30" ry="20"></ellipse>
</g>

<g id='othergroup2' class='othergroup'>
  <title>othergroup2</title>
  <text id='text2'>textContent2</text>
  <ellipse fill="#23d5f6" cx="198.5" cy="25" rx="27" ry="18"></ellipse>
</g>
</svg> 

Конечно, мы пропускаем привязку данных и используем selection.remove() с selection.each() для добавления удаленных элементов к другому родителю:

 var ellipse = svg.selectAll("ellipse")
  .remove()
  .each(function() {
     svg.select("#maingroup").append(()=>this);
  })
 
 var svg = d3.select("svg");

var ellipse = svg.selectAll("ellipse")
  .remove()
  .each(function() {
     svg.select("#maingroup").append(()=>this);
  }) 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg>
<g id='maingroup' class='main'>
  <title>titlemain</main>
  <text id='text'>textContent</text>
</g>

<g id='othergroup1' class='othergroup'>
  <title>othergroup1</title>
  <text id='text1'>textContent1</text>
  <ellipse fill="#ac08b6" cx="198.5" cy="25" rx="30" ry="20"></ellipse>
</g>

<g id='othergroup2' class='othergroup'>
  <title>othergroup2</title>
  <text id='text2'>textContent2</text>
  <ellipse fill="#23d5f6" cx="198.5" cy="25" rx="27" ry="18"></ellipse>
</g>
</svg> 

Использование selection.insert() вместо selection.append() может обеспечить немного большую гибкость с точки зрения упорядочения добавляемых элементов.

Наконец, адаптируя ваш код с минимальными изменениями, мы можем просто использовать selection.append() с функцией, возвращающей узел в сочетании с циклом for:

 var ellipses = svg.selectAll("ellipse").remove();
for(ellipse of ellipses) svg.select("#maingroup").append(()=>ellipse);
 
 var svg = d3.select("svg");

var ellipses = svg.selectAll("ellipse").remove();
for(ellipse of ellipses) svg.select("#maingroup").append(()=>ellipse); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
<svg>
<g id='maingroup' class='main'>
  <title>titlemain</main>
  <text id='text'>textContent</text>
</g>

<g id='othergroup1' class='othergroup'>
  <title>othergroup1</title>
  <text id='text1'>textContent1</text>
  <ellipse fill="#ac08b6" cx="198.5" cy="30" rx="30" ry="20"></ellipse>
</g>

<g id='othergroup2' class='othergroup'>
  <title>othergroup2</title>
  <text id='text2'>textContent2</text>
  <ellipse fill="#23d5f6" cx="198.5" cy="25" rx="27" ry="18"></ellipse>
</g>
</svg> 

Конечно, предварительный выбор основной группы будет более эффективным, чем выбор ее на каждой итерации, и простой javascript должен быть более производительным.