Как я могу привести круг вперед с d3?

Прежде всего, я использую d3.js для отображения кругов разного размера в массивах. Наведите указатель мыши на ящик, я хочу, чтобы круг был увязан, чтобы стать больше, что я могу сделать, но я понятия не имею, как перенести его на фронт. В настоящее время, когда он отображается, он скрывается за несколькими другими кругами. Как я могу это исправить?

Вот пример кода:

   .on("mouseover", function() { 
    d3.select(this).attr("r", function(d) { return 100; })
  })

Я попытался использовать методы сортировки и порядка, но они не сработали. Я почти уверен, что не сделал это правильно. Любые мысли?

Ответ 1

Вам придется изменить порядок объекта и сделать круг, наведенный мышью, последним добавленным элементом. Как вы можете видеть здесь: https://gist.github.com/3922684 и как предложено nautat, вы должны определить следующее перед вашим основным сценарием:

d3.selection.prototype.moveToFront = function() {
  return this.each(function(){
    this.parentNode.appendChild(this);
  });
};

Затем вам нужно будет просто вызвать функцию moveToFront для вашего объекта (скажем, circles) при наведении курсора мыши:

circles.on("mouseover",function(){
  var sel = d3.select(this);
  sel.moveToFront();
});

Изменить: Как подсказал Хенрик Нордберг, необходимо связать данные с DOM, используя второй аргумент .data(). Это необходимо, чтобы не потерять привязку к элементам. Пожалуйста, прочитайте ответ хенрика (и подтвердите его!) Для получения дополнительной информации. В качестве общего совета всегда используйте второй аргумент .data() при привязке данных к DOM, чтобы использовать все возможности производительности d3.


Изменить: Как упомянул Клеменс Толбом, обратная функция будет такой:

d3.selection.prototype.moveToBack = function() {
    return this.each(function() {
        var firstChild = this.parentNode.firstChild;
        if (firstChild) {
            this.parentNode.insertBefore(this, firstChild);
        }
    });
};

Ответ 2

Если вы используете метод moveToFront(), убедитесь, что вы указываете второй аргумент метода соединения data() или ваши данные будут не синхронизированы с вашей DOM.

d3.js объединяет ваши данные (предоставляемые parse()) с создаваемыми элементами DOM. Если вы манипулируете DOM, как было предложено выше, d3.js не будет знать, какой элемент DOM соответствует какой точке данных, если вы не укажете уникальное значение для каждой точки данных в вызове data() как ссылка на API показывает:

.data(data, function(d) { return d.name; })

Ответ 3

В версии d3 версии 4 есть набор встроенных функций, которые обрабатывают этот тип поведения без необходимости его реализации самостоятельно. Для получения дополнительной информации см. Документацию d3v4.

Короче говоря, чтобы привести элемент вперед, используйте selection.raise()

selection.raise()

Вставляет каждый выбранный элемент в порядке, в качестве последнего дочернего элемента его родителя. Эквивалентно:

selection.each(function() {
   this.parentNode.appendChild(this);
   });

Ответ 4

Из моего мучительного опыта работы с IE9 использование parentNode.appendChild может привести к потерям обработчиков событий в узлах. Поэтому я склонен использовать другой подход, то есть сортировку узлов так, чтобы выбранная была выше остальных:

     .on("mouseover", function(selected) {
        vis.selectAll('.node')
        .sort(function(a, b) {
          if (a.id === selected.id) {
            return 1;
          } else {
            if (b.id === selected.id) {
              return -1;
            } else {
              return 0;
            }
          }
        });
      })