D3でSVG要素のZインデックスを更新する


81

D3ライブラリを使用してSVG要素をzオーダーの最上位に移動する効果的な方法は何ですか?

私の特定のシナリオは、マウスが特定の部分の上にあるときに(にを追加strokeしてpath)強調表示する円グラフです。チャートを生成するためのコードブロックは次のとおりです。

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);
    });

私はいくつかのオプションを試しましたが、今のところ運がありません。両方の使用style("z-index")と呼び出しclassedは機能しませんでした。

「トップ」クラスは、私のCSSでは次のように定義されています。

.top {
    fill: red;
    z-index: 100;
}

このfillステートメントは、正しくオン/オフになっていることを確認するためのものです。です。

使用sortはオプションだと聞きましたが、「選択された」要素を一番上に表示するためにどのように実装されるかはわかりません。

更新:

次のコードで特定の状況を修正しました。このコードは、mouseoverイベントのSVGに新しいアークを追加して、ハイライトを表示します。

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();
    });

回答:


52

開発者が提示した解決策の1つは、「D3のソート演算子を使用して要素を並べ替える」です。(https://github.com/mbostock/d3/issues/252を参照)

この観点から、データを比較することによって要素を並べ替えるか、データのない要素の場合は位置を並べ替えることができます。

.on("mouseover", function(d) {
    svg.selectAll("path").sort(function (a, b) { // select the parent and sort the path's
      if (a.id != d.id) return -1;               // a is not the hovered element, send "a" to the back
      else return 1;                             // a is the hovered element, bring "a" to the front
  });
})

これはZオーダーで機能するだけでなく、要素を上げた直後に開始されたドラッグを混乱させることもありません。
オリバーボック2012

3
それは私にとってもうまくいきません。a、bはデータのみであり、d3選択要素またはDOM要素ではないようです
nkint 2013年

比較する際に実際に、@nkintのためのようにそれがbeのために動作しませんでしたa.idd.id。解決策は、直接比較することであるad
平和はたっぷり2014年

これは、多数の子要素に対して適切に機能しません。後続ので発生した要素を再レンダリングすることをお勧めします<g>
部族バイブ2014年

1
あるいは、svg:use要素は隆起した要素を動的に指すことができます。解決策については、stackoverflow.com / a / 6289809/292085を参照してください。
部族バイブ2014年

91

他の回答で説明されているように、SVGにはz-indexの概念がありません。代わりに、ドキュメント内の要素の順序によって、図面内の順序が決まります。

要素を手動で並べ替える以外に、特定の状況では別の方法があります。

D3を使用すると、特定の種類の要素が常に他の種類の要素の上に描画されることがよくあります

たとえば、グラフをレイアウトする場合、リンクは常にノードの下に配置する必要があります。より一般的には、一部の背景要素は通常、他のすべての下に配置する必要がありますが、一部のハイライトとオーバーレイは上に配置する必要があります。

このような状況の場合は、それらの要素グループの親グループ要素を作成するのが最善の方法であることがわかりました。SVGでは、gそのための要素を使用できます。たとえば、常にノードの下に配置する必要があるリンクがある場合は、次のようにします。

svg.append("g").attr("id", "links")
svg.append("g").attr("id", "nodes")

ここで、リンクとノードをペイントするときに、次のように選択します(#要素IDを参照して始まるセレクター)。

svg.select("#links").selectAll(".link")
// add data, attach elements and so on

svg.select("#nodes").selectAll(".node")
// add data, attach elements and so on

これで、すべてのリンクは常にすべてのノード要素の前に構造的に追加されます。したがって、SVGは、要素を追加または削除する頻度と順序に関係なく、すべてのノードの下にすべてのリンクを表示します。もちろん、同じタイプの(つまり、同じコンテナ内の)すべての要素は、追加された順序に従います。


1
私の本当の問題が私の要素をグループに編成することであったとき、私は私の頭の中で1つのアイテムを上に動かすことができるという考えでこれに遭遇しました。「実際の」問題が個々の要素の移動である場合は、前の回答で説明した並べ替え方法が適しています。
ジョンデビッドファイブ

驚くばかり!ありがとう、notan3xit、あなたは私の日を救った!他の人のためにアイデアを追加するだけです-目的の可視性の順序とは異なる順序で要素を追加する必要がある場合は、適切な可視性の順序でグループを作成し(要素を追加する前でも)、これらに要素を追加できます任意の順序でグループ化します。
Olga Gnatenko 2014年

1
これは正しいアプローチです。考えてみれば明らかです。
デイブ

私にとっては、svg.select( '#links')...をd3.select( '#links')...に置き換える必要がありました。多分これは他の人を助けるでしょう。
交換

26

SVGにはZインデックスがありませんが、DOM要素の順序を使用するため、次の方法でSVGを前面に表示できます。

this.parentNode.appendChild(this);

次に、たとえば、insertBeforeを使用して元に戻すことができmouseoutます。ただし、これには、要素を前に挿入する必要がある兄弟ノードをターゲットにできる必要があります。

デモ:このJSFiddleを見てください


Firefoxが:hoverスタイルのレンダリングに失敗していなければ、これは素晴らしい解決策になるでしょう。また、このユースケースでは、最後のìnsert関数は必要ないと思います。
John Slegers 2014年

@swenedo、あなたのソリューションは私にとって素晴らしい働きをし、要素を前面に押し出します。後ろに持っていくということに関しては、少し積み重なっていて、正しい書き方がわかりません。オブジェクトを後ろに戻す方法の例を投稿してください。ありがとう。
マイクB.

1
@MikeB。確かに、私はちょうど私の答えを更新しました。d3のinsert関数は、新しい要素を作成するため、おそらく最善の方法ではないことに気づきました。既存の要素を移動したいだけなので、insertBefore代わりにこの例で使用します。
swenedo 2015

この方法を使おうとしましたが、残念ながらIE11では動作しません。このブラウザの回避策はありますか?
アントニオジョバンニスキアボーネ2016

13

SVGはz-indexを行いません。Z順序は、コンテナー内のSVGDOM要素の順序によって決まります。

私が知る限り(そして私は過去にこれを数回試しました)、D3はそれを前面に出すために単一の要素を取り外したり再取り付けしたりする方法を提供していません。

選択範囲に表示される順序に一致するようにノードを再シャッフルする.order()メソッドがあります。あなたの場合、あなたは単一の要素を前面に持ってくる必要があります。したがって、技術的には、目的の要素を前に(または最後に、どちらが一番上か思い出せない)選択を再利用して、それを呼び出すorder()ことができます。

または、このタスクでd3をスキップし、プレーンJS(またはjQuery)を使用してその単一のDOM要素を再挿入することもできます。


5
一部のブラウザでは、マウスオーバーハンドラ内の要素の削除/挿入に問題があることに注意してください。マウスアウトイベントが正しく送信されない可能性があるため、すべてのブラウザでこれを試して、期待どおりに機能することを確認することをお勧めします。
エリク・ダルストローム

順序を変更すると、円グラフのレイアウトも変更されませんか?また、要素を再挿入する方法を学ぶためにjQueryを掘り下げています。
邪悪なクローゼットモンキー

選択したソリューションで質問を更新しましたが、実際には元の質問のコアを利用していません。このソリューションについて本当に悪いことがあれば、コメントを歓迎します!元の質問についての洞察をありがとう。
邪悪なクローゼットモンキー

主にオーバーレイされた形状に塗りつぶしがないという事実のおかげで、合理的なアプローチのように見えます。もしそうなら、私はそれが現れた瞬間にマウスイベントを「盗む」ことを期待します。そして、@ErikDahlströmの主張は今も続いていると思います。これは一部のブラウザー(主にIE9)では依然として問題になる可能性があります。追加の「安全性」の.style('pointer-events', 'none')ために、オーバーレイされたパスに追加できます。残念ながら、このプロパティはIEでは何もしませんが、それでも....
meetamit 2012年

@EvilClosetMonkeyこんにちは...この質問には多くの意見が寄せられています。以下のfuturendの回答は、私よりも役立つと思います。したがって、受け入れられた回答をfuturendの回答に変更することを検討してください。そうすれば、上位に表示されます。
Meetamit 2013年


4

私はfuturendのソリューションをコードに実装し、それは機能しましたが、使用している要素の数が多いため、非常に低速でした。これは、私の特定の視覚化でより高速に機能するjQueryを使用した代替方法です。これは、共通のクラスを持つ上に必要なsvgに依存しています(私の例では、クラスはデータセットにd.keyとして示されています)。私のコードには、<g>再編成しているすべてのSVGを含むクラス「locations」を持つがあります。

.on("mouseover", function(d) {
    var pts = $("." + d.key).detach();
    $(".locations").append(pts);
 });

したがって、特定のデータポイントにカーソルを合わせると、コードはその特定のクラスのSVGDOM要素を持つ他のすべてのデータポイントを検索します。次に、それらのデータポイントに関連付けられているSVGDOM要素を切り離して再挿入します。


4

まったく新しい答えを書き出すのではなく、@ notan3xitが答えたものを拡張したかった(しかし、私には十分な評判がありません)。

要素の順序の問題を解決する別の方法は、描画時に「追加」ではなく「挿入」を使用することです。そうすれば、パスは常に他のsvg要素の前に一緒に配置されます(これは、コードが他のsvg要素のenter()の前にリンクのenter()をすでに実行していることを前提としています)。

d3挿入API:https//github.com/mbostock/d3/wiki/Selections#insert


1

既存のSVGでZオーダーを微調整する方法を見つけるのに何年もかかりました。ツールチップの動作を伴うd3.brushのコンテキストで本当に必要でした。2つの機能をうまく連携させるには(http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html)、d3.brushがZオーダーの最初である必要があります(最初にキャンバスに描画され、次に残りのSVG要素で覆われます)そして、その上にあるものに関係なく、すべてのマウスイベントをキャプチャします(より高いZインデックスを使用)。

ほとんどのフォーラムのコメントでは、最初にd3.brushをコードに追加してから、SVGの「描画」コードを追加する必要があると述べています。しかし、私にとっては、外部SVGファイルをロードしたのでそれは不可能でした。いつでも簡単にブラシを追加し、後でZの順序を変更できます。

d3.select("svg").insert("g", ":first-child");

d3.brushセットアップのコンテキストでは、次のようになります。

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);

d3.js insert()関数API:https//github.com/mbostock/d3/wiki/Selections#insert

お役に立てれば!


0

バージョン1

理論的には、次のことがうまくいくはずです。

CSSコード:

path:hover {
    stroke: #fff;
    stroke-width : 2;
}

このCSSコードは、選択したパスにストロークを追加します。

JSコード:

svg.selectAll("path").on("mouseover", function(d) {
    this.parentNode.appendChild(this);
});

このJSコードは、最初にDOMツリーからパスを削除し、次にそのパスをその親の最後の子として追加します。これにより、同じ親の他のすべての子の上にパスが描画されます。

実際には、このコードはChromeでは正常に機能しますが、他の一部のブラウザでは機能しません。LinuxMintマシンのFirefox20で試しましたが、動作しませんでした。どういうわけか、Firefoxはトリガーに失敗します:hoverスタイルず、これを修正する方法が見つかりませんでした。


バージョン2

そこで私は別の方法を思いついた。それは少し「汚い」かもしれませんが、少なくともそれは機能し、すべての要素をループする必要はありません(他のいくつかの答えのように)。

CSSコード:

path.hover {
    stroke: #fff;
    stroke-width : 2;
}

:hover疑似セレクターを使用する代わりに、.hoverクラス

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);
   })

マウスオーバー時に、.hoverクラスをパスに追加します。マウスアウト時に、私はそれを削除します。最初の場合と同様に、コードはDOMツリーからパスを削除し、それを親の最後の子として追加します。


0

あなたはマウスオーバーでこのようにすることができますあなたはそれを上に引っ張ることができます。

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();
});

プッシュバックしたい場合

nodes.on("mouseout",function(){
   d3.select(this).pushElementAsBackLayer();
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.