В D3.js порядок DOM-элементов напрямую влияет на визуальное отображение графики. SVG-элементы, находящиеся ниже в DOM-дереве, рисуются поверх предыдущих. Поэтому сортировка элементов становится важной частью построения интерактивных визуализаций.
Методы .sort() и .order() позволяют:
Несмотря на внешнюю схожесть, методы решают разные задачи:
| Метод | Назначение |
|---|---|
.sort() |
Сортирует элементы по данным |
.order() |
Приводит DOM-порядок к порядку selection |
.sort()selection.sort(compare);
compareФункция сравнения работает аналогично
Array.prototype.sort():
(a, b) => число
Возвращаемое значение:
| Результат | Поведение |
|---|---|
< 0 |
a раньше b |
> 0 |
b раньше a |
0 |
порядок не меняется |
.sort()Метод:
Это важно: .sort() изменяет не только внутренний порядок
selection, но и структуру DOM.
const data = [8, 3, 15, 6, 1];
d3.select("body")
.selectAll("div")
.data(data)
.enter()
.append("div")
.text(d => d);
Результат:
<div>8</div>
<div>3</div>
<div>15</div>
<div>6</div>
<div>1</div>
d3.selectAll("div")
.sort((a, b) => a - b);
Результат:
<div>1</div>
<div>3</div>
<div>6</div>
<div>8</div>
<div>15</div>
d3.selectAll("div")
.sort((a, b) => b - a);
const users = [
{ name: "Анна", age: 28 },
{ name: "Игорь", age: 19 },
{ name: "Мария", age: 34 }
];
d3.select("body")
.selectAll("p")
.data(users)
.enter()
.append("p")
.text(d => `${d.name}: ${d.age}`);
d3.selectAll("p")
.sort((a, b) => a.age - b.age);
.sort((a, b) => d3.ascending(a.name, b.name))
.sort((a, b) => d3.descending(a.name, b.name))
d3.ascending() и d3.descending()D3 предоставляет готовые компараторы.
d3.ascending(a, b)Возвращает:
-1, если a < b1, если a > b0, если равныПример:
.sort((a, b) => d3.ascending(a.value, b.value))
d3.descending(a, b)Обратная сортировка:
.sort((a, b) => d3.descending(a.value, b.value))
Это один из наиболее важных сценариев использования.
const data = [
{ r: 10 },
{ r: 40 },
{ r: 25 }
];
const svg = d3.select("svg");
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", d => d.r);
SVG рисует элементы сверху вниз по DOM.
Если большой круг находится раньше маленького, маленький может оказаться скрытым.
.sort()svg.selectAll("circle")
.sort((a, b) => b.r - a.r);
Теперь большие окружности окажутся под маленькими.
const data = [12, 5, 18, 7, 22];
const svg = d3.select("svg");
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * 40)
.attr("y", d => 200 - d * 5)
.attr("width", 30)
.attr("height", d => d * 5);
svg.selectAll("rect")
.sort((a, b) => b - a);
Однако визуально позиции не изменятся автоматически, потому что
координаты x уже заданы.
.sort() изменяет DOM-порядок, но не вычисленные
координаты.
Обычно сортировка сопровождается анимацией.
svg.selectAll("rect")
.sort((a, b) => b - a)
.transition()
.duration(1000)
.attr("x", (d, i) => i * 40);
Теперь столбцы плавно перестроятся.
.sort()
внутриУпрощённо механизм выглядит так:
insertBefore.Следовательно:
JavaScript-сортировка в современных движках стабильна, но исторически это было не всегда.
Если элементы равны:
.sort((a, b) => a.value - b.value)
то одинаковые значения обычно сохраняют исходный порядок.
Один из ключевых сценариев D3 — обновление визуализации.
function update(data) {
const bars = svg.selectAll("rect")
.data(data);
bars.enter()
.append("rect")
.merge(bars)
.sort((a, b) => b - a)
.transition()
.attr("x", (d, i) => i * 40);
}
.order().order() переставляет DOM-элементы в соответствии с
текущим порядком selection.
Синтаксис:
selection.order();
Метод не сортирует данные самостоятельно.
.order()Основной сценарий:
.sort() от
.order().sort().order().order()const data = [
{ id: 1, value: 20 },
{ id: 2, value: 5 },
{ id: 3, value: 12 }
];
const items = d3.select("body")
.selectAll("div")
.data(data)
.enter()
.append("div")
.text(d => d.value);
const sorted = items.data()
.sort((a, b) => a.value - b.value);
DOM пока не изменился.
.order()items.order();
Теперь DOM будет соответствовать текущему порядку selection.
.raise() и .lower()D3 содержит методы управления слоями:
| Метод | Действие |
|---|---|
.raise() |
Поднимает элемент наверх |
.lower() |
Опускает вниз |
После множества таких операций DOM может стать неупорядоченным.
.order() помогает восстановить корректную структуру.
При data join порядок элементов особенно важен.
const update = svg.selectAll("circle")
.data(data, d => d.id);
После обновления:
update.order();
Это гарантирует правильный порядок элементов.
const data = [
{ r: 20 },
{ r: 50 },
{ r: 10 },
{ r: 35 }
];
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", 150)
.attr("cy", 150)
.attr("r", d => d.r);
svg.selectAll("circle")
.sort((a, b) => b.r - a.r);
Большие круги будут сзади, маленькие — спереди.
d3.select("button")
.on("click", () => {
svg.selectAll("rect")
.sort((a, b) => b - a)
.transition()
.duration(800)
.attr("x", (d, i) => i * 40);
});
.sort((a, b) => {
if (a.score !== b.score) {
return b.score - a.score;
}
return d3.ascending(a.name, b.name);
})
DOM-перестановки — дорогая операция.
Лучше:
.sort() внутри анимаций;Иногда эффективнее сортировать массив заранее:
data.sort((a, b) => b.value - a.value);
А затем выполнять обычный data join.
Ошибка:
.sort((a, b) => b - a);
Позиции элементов не меняются автоматически.
Нужно отдельно обновлять:
.attr("x", ...)
.attr("y", ...)
.sort() и .order().sort():
selection.sort(...)
.order():
selection.order()
Первый принимает компаратор, второй — нет.
.sort() использует данные элементов.
Если данных нет:
d3.selectAll("div").sort();
результат будет бессмысленным либо приведёт к ошибкам.
При использовании transition порядок операций важен.
Правильно:
selection
.sort(...)
.transition();
Неправильно:
selection
.transition()
.sort(...);
SVG не поддерживает CSS z-index так, как HTML.
Порядок элементов определяется исключительно DOM-позицией.
Именно поэтому .sort(), .order(),
.raise() и .lower() играют критическую роль в
SVG-визуализациях.
.filter() и
.sort()svg.selectAll("circle")
.filter(d => d.active)
.sort((a, b) => b.value - a.value);
Сортируются только активные элементы.
В force-directed graph порядок влияет на читаемость:
nodes.sort((a, b) => b.radius - a.radius);
Мелкие узлы оказываются поверх крупных.
При отображении географических областей:
paths.sort((a, b) => b.population - a.population);
мелкие регионы могут отображаться поверх крупных.
При работе через <canvas> методы
.sort() и .order() напрямую не управляют
отрисовкой.
Но сортировка данных перед рендером остаётся важной:
data.sort((a, b) => a.depth - b.depth);
Selection в D3 — это массив групп элементов.
.sort() изменяет:
.order() работает только с DOM-позицией.
.sort((a, b) => 0)
DOM обычно остаётся неизменным.
Но D3 всё равно может выполнять проверку порядка.
function sortBars() {
svg.selectAll("rect")
.sort((a, b) => b.value - a.value)
.transition()
.duration(1000)
.attr("x", (d, i) => i * 50);
}
svg.selectAll("circle")
.sort((a, b) => b.r - a.r);
selection.order();
| Особенность | .sort() |
.order() |
|---|---|---|
| Сортирует данные | Да | Нет |
| Требует compare-функцию | Да | Нет |
| Меняет DOM | Да | Да |
| Меняет порядок selection | Да | Нет |
| Используется для слоёв SVG | Да | Да |
| Подходит для анимированной перестройки | Да | Нет |