Jaki jest skuteczny sposób na umieszczenie elementu SVG na szczycie kolejności z przy użyciu biblioteki D3?
Mój konkretny scenariusz to wykres kołowy, który podkreśla (dodając a stroke
do path
), kiedy wskaźnik myszy znajduje się nad danym kawałkiem. Blok kodu do generowania mojego wykresu znajduje się poniżej:
svg.selectAll("path")
.data(d)
.enter().append("path")
.attr("d", arc)
.attr("class", "arc")
.attr("fill", function(d) { return color(d.name); })
.attr("stroke", "#fff")
.attr("stroke-width", 0)
.on("mouseover", function(d) {
d3.select(this)
.attr("stroke-width", 2)
.classed("top", true);
//.style("z-index", 1);
})
.on("mouseout", function(d) {
d3.select(this)
.attr("stroke-width", 0)
.classed("top", false);
//.style("z-index", -1);
});
Wypróbowałem kilka opcji, ale na razie bez powodzenia. Używanie style("z-index")
i wywoływanie classed
obu nie działało.
„Najwyższa” klasa jest zdefiniowana w moim CSS w następujący sposób:
.top {
fill: red;
z-index: 100;
}
fill
Stwierdzenie jest tam, aby upewnić się, że wiedział, że włączanie / wyłączanie poprawnie. To jest.
Słyszałem, że użycie sort
jest opcją, ale nie jestem pewien, w jaki sposób zostanie zaimplementowana w celu przeniesienia „wybranego” elementu na górę.
AKTUALIZACJA:
Naprawiłem moją szczególną sytuację za pomocą następującego kodu, który dodaje nowy łuk do pliku SVG na mouseover
wydarzeniu, aby pokazać wyróżnienie.
svg.selectAll("path")
.data(d)
.enter().append("path")
.attr("d", arc)
.attr("class", "arc")
.style("fill", function(d) { return color(d.name); })
.style("stroke", "#fff")
.style("stroke-width", 0)
.on("mouseover", function(d) {
svg.append("path")
.attr("d", d3.select(this).attr("d"))
.attr("id", "arcSelection")
.style("fill", "none")
.style("stroke", "#fff")
.style("stroke-width", 2);
})
.on("mouseout", function(d) {
d3.select("#arcSelection").remove();
});
źródło
a.id
id.id
. Rozwiązaniem jest bezpośrednie porównaniea
id
.<g>
.svg:use
element może wskazywać dynamicznie na podniesiony element. Rozwiązanie znajdziesz na stackoverflow.com/a/6289809/292085 .Jak wyjaśniono w innych odpowiedziach, SVG nie ma pojęcia indeksu z. Zamiast tego kolejność elementów w dokumencie określa kolejność na rysunku.
Oprócz ręcznej zmiany kolejności elementów istnieje inny sposób w określonych sytuacjach:
Pracując z D3 często masz pewne typy elementów, które zawsze powinny być narysowane na innych typach elementów .
Na przykład podczas układania wykresów linki należy zawsze umieszczać poniżej węzłów. Mówiąc bardziej ogólnie, niektóre elementy tła zwykle należy umieścić pod wszystkim innym, a niektóre podświetlenia i nakładki należy umieścić powyżej.
Jeśli masz tego rodzaju sytuację, stwierdziłem, że tworzenie elementów grupy nadrzędnej dla tych grup elementów jest najlepszym sposobem. W SVG możesz użyć do tego
g
elementu. Na przykład, jeśli masz łącza, które zawsze powinny być umieszczane pod węzłami, wykonaj następujące czynności:svg.append("g").attr("id", "links") svg.append("g").attr("id", "nodes")
Teraz, kiedy malujesz swoje linki i węzły, wybierz w następujący sposób (selektory rozpoczynające się
#
od identyfikatora elementu):svg.select("#links").selectAll(".link") // add data, attach elements and so on svg.select("#nodes").selectAll(".node") // add data, attach elements and so on
Teraz wszystkie linki będą zawsze dołączane strukturalnie przed wszystkimi elementami węzła. W ten sposób SVG pokaże wszystkie linki poniżej wszystkich węzłów, bez względu na to, jak często i w jakiej kolejności dodasz lub usuniesz elementy. Oczywiście wszystkie elementy tego samego typu (czyli w tym samym kontenerze) nadal będą podlegały kolejności, w jakiej zostały dodane.
źródło
Ponieważ SVG nie ma indeksu Z, ale używa kolejności elementów DOM, możesz go przenieść na pierwszy plan przez:
this.parentNode.appendChild(this);
Możesz wtedy np. Skorzystać z insertBefore przed ponownym założeniem
mouseout
. Wymaga to jednak, abyś był w stanie wskazać węzeł rodzeństwa, który chcesz wstawić wcześniej.DEMO: Spójrz na to JSFiddle
źródło
:hover
stylów. Nie sądzę również, aby funkcja ìnsert na końcu była wymagana w tym przypadku użycia.insert
funkcja d3 prawdopodobnie nie jest najlepszym rozwiązaniem, ponieważ tworzy nowy element. Chcemy tylko przenieść istniejący element, dlategoinsertBefore
zamiast tego używam w tym przykładzie.SVG nie wykonuje indeksu Z. Porządek Z jest podyktowany kolejnością elementów SVG DOM w ich kontenerze.
O ile mogłem powiedzieć (i próbowałem tego kilka razy w przeszłości), D3 nie zapewnia metod odłączania i ponownego mocowania pojedynczego elementu w celu przesunięcia go na przód lub tak dalej.
Istnieje
.order()
metoda , która przetasowuje węzły w celu dopasowania ich do kolejności, w jakiej pojawiają się w zaznaczeniu. W twoim przypadku musisz przenieść jeden element na przód. Więc technicznie rzecz biorąc, możesz dokonać wyboru z żądanym elementem na początku (lub na końcu, nie pamiętając, który jest najwyższy), a następnie go wywołaćorder()
.Możesz też pominąć d3 w tym zadaniu i użyć zwykłego JS (lub jQuery), aby ponownie wstawić ten pojedynczy element DOM.
źródło
.style('pointer-events', 'none')
do nałożonej ścieżki. Niestety ta właściwość nic nie robi w IE, ale mimo to ....Prostą odpowiedzią jest użycie metod zamawiania d3. Oprócz d3.select ('g'). Order (), w wersji 4 dostępne są .lower () i .raise (). Zmienia to wygląd elementów. Więcej informacji można znaleźć w dokumentacji - https://github.com/d3/d3/blob/master/API.md#selections-d3-selection
źródło
Wdrożyłem rozwiązanie futurend w swoim kodzie i działało, ale przy dużej liczbie elementów, których używałem, działało bardzo wolno. Oto alternatywna metoda wykorzystująca jQuery, która działała szybciej w przypadku mojej konkretnej wizualizacji. Polega na tym, że pliki SVG, które chcesz, mają wspólną klasę (w moim przykładzie klasa jest zapisana w moim zestawie danych jako d.key). W moim kodzie znajduje się
<g>
z klasą „lokalizacje”, która zawiera wszystkie pliki SVG, które reorganizuję..on("mouseover", function(d) { var pts = $("." + d.key).detach(); $(".locations").append(pts); });
Więc kiedy najedziesz kursorem na konkretny punkt danych, kod znajdzie wszystkie inne punkty danych z elementami SVG DOM z tą konkretną klasą. Następnie odłącza i ponownie wstawia elementy SVG DOM powiązane z tymi punktami danych.
źródło
Chciałem rozszerzyć to, na co odpowiedział @ notan3xit, zamiast pisać całą nową odpowiedź (ale nie mam wystarczającej reputacji).
Innym sposobem rozwiązania problemu z kolejnością elementów jest użycie opcji „wstaw” zamiast „dołącz” podczas rysowania. W ten sposób ścieżki będą zawsze umieszczane razem przed innymi elementami svg (zakłada to, że twój kod już wykonuje enter () dla linków przed enter () dla innych elementów svg).
d3 wstaw api: https://github.com/mbostock/d3/wiki/Selections#insert
źródło
Wieki zajęło mi znalezienie sposobu na poprawienie kolejności Z w istniejącym SVG. Naprawdę potrzebowałem tego w kontekście d3.brush z zachowaniem podpowiedzi. Aby te dwie funkcje dobrze ze sobą współpracowały ( http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html ), musisz mieć d3.brush jako pierwszy w kolejności Z (Pierwsza do narysowania na płótnie, a następnie zakryta przez pozostałe elementy SVG) i przechwytuje wszystkie zdarzenia myszy, bez względu na to, co jest na wierzchu (z wyższymi indeksami Z).
Większość komentarzy na forum mówi, że najpierw należy dodać plik d3.brush do kodu, a następnie kod „rysujący” SVG. Ale dla mnie nie było to możliwe, ponieważ załadowałem zewnętrzny plik SVG. Możesz łatwo dodać pędzel w dowolnym momencie i zmienić kolejność Z później za pomocą:
d3.select("svg").insert("g", ":first-child");
W kontekście konfiguracji d3.brush będzie wyglądać następująco:
brush = d3.svg.brush() .x(d3.scale.identity().domain([1, width-1])) .y(d3.scale.identity().domain([1, height-1])) .clamp([true,true]) .on("brush", function() { var extent = d3.event.target.extent(); ... }); d3.select("svg").insert("g", ":first-child"); .attr("class", "brush") .call(brush);
API funkcji insert () d3.js: https://github.com/mbostock/d3/wiki/Selections#insert
Mam nadzieję że to pomoże!
źródło
Wersja 1
Teoretycznie poniższe powinny działać dobrze.
Kod CSS:
path:hover { stroke: #fff; stroke-width : 2; }
Ten kod CSS doda obrys do wybranej ścieżki.
Kod JS:
svg.selectAll("path").on("mouseover", function(d) { this.parentNode.appendChild(this); });
Ten kod JS najpierw usuwa ścieżkę z drzewa DOM, a następnie dodaje ją jako ostatnie dziecko swojego rodzica. Daje to pewność, że ścieżka jest rysowana na wszystkich innych elementach potomnych tego samego rodzica.
W praktyce ten kod działa dobrze w Chrome, ale psuje się w niektórych innych przeglądarkach. Wypróbowałem to w Firefoksie 20 na moim komputerze z Linux Mint i nie mogłem go uruchomić. W jakiś sposób Firefox nie uruchamia
:hover
stylów i nie znalazłem sposobu, aby to naprawić.Wersja 2
Więc wymyśliłem alternatywę. Może być trochę „brudny”, ale przynajmniej działa i nie wymaga zapętlania wszystkich elementów (jak niektóre inne odpowiedzi).
Kod CSS:
path.hover { stroke: #fff; stroke-width : 2; }
Zamiast korzystać z
:hover
pseudoselektora, używam.hover
klasyKod JS:
svg.selectAll(".path") .on("mouseover", function(d) { d3.select(this).classed('hover', true); this.parentNode.appendChild(this); }) .on("mouseout", function(d) { d3.select(this).classed('hover', false); })
Po najechaniu myszą dodaję
.hover
klasę do mojej ścieżki. Po wyprowadzeniu myszy usuwam go. Podobnie jak w pierwszym przypadku, kod usuwa również ścieżkę z drzewa DOM, a następnie dodaje ją jako ostatnie dziecko swojego rodzica.źródło
Możesz to zrobić Po najechaniu myszą Możesz wyciągnąć go do góry.
d3.selection.prototype.bringElementAsTopLayer = function() { return this.each(function(){ this.parentNode.appendChild(this); }); }; d3.selection.prototype.pushElementAsBackLayer = function() { return this.each(function() { var firstChild = this.parentNode.firstChild; if (firstChild) { this.parentNode.insertBefore(this, firstChild); } });
};
nodes.on("mouseover",function(){ d3.select(this).bringElementAsTopLayer(); });
Jeśli chcesz odepchnąć
nodes.on("mouseout",function(){ d3.select(this).pushElementAsBackLayer(); });
źródło