2013-05-28 19 views
8

Buduję wizualizację mapy za pomocą d3.js. Rysuję wypełnione wielokąty zarówno dla stanów USA, jak i dla hrabstw. Warstwa SVG dla powiatów znajduje się pod warstwą dla stanów. Stany są wypełnione, ale wypełnienie-krycie jest ustawione na 0; wypełnienie jest wymagane (chyba) do przechwytywania zdarzeń kliknięcia.Obsługa zdarzeń myszy w nakładających się warstwach SVG

Chcę przechwytywać zdarzenia kliknięć na poziomie stanu, ale chcę przechwycić zdarzenia najeżdżania myszką na poziomie powiatu.

Jednak zdarzenie mouseover jest przechwytywane przez stany i nie jest przekazywane do powiatów.

Czy istnieje jakiś sposób przekazania zdarzenia w dół warstwy lub w inny sposób wywołania zdarzenia mouseover w hrabstwie?

+0

Czy masz link do przykładu, którego używasz? –

Odpowiedz

2

Jeśli chcesz coś zrobić, gdy klikniesz stan, np. Zmieniając styl obramowania lub krycie kształtu, możesz użyć faktu, że każdy okręg należy do stanu i po prostu grupuje powiaty w stanach, przechwytując zdarzenie "kliknij" w hrabstwie, wybierz odpowiedni element stanu i zmień jego atrybuty wizualne. Makieta tej strategii:

// Setup the visualization, assuming that you have a div with id 'chart' 
var width = 300, 
    height = 300, 
    div = d3.select('#chart'), 
    svg = div.append('svg') 
     .attr('width', width) 
     .attr('height', height); 

// Array of states, each one containing one or more counties. In this example, 
// the states are rectangles, but you get the idea 
var states = [ 
    { 
     width: 300, 
     height: 300, 
     name: 'Fake State', 
     counties: [ 
      {x: 5, y: 5, width: 95, height: 290, fill: '#b4a0a0'}, 
      {x: 100, y: 5, width: 150, height: 290, fill: '#b4c0a0'}, 
      {x: 250, y: 5, width: 45, height: 290, fill: '#a4a0c0'} 
     ] 
    }]; 

// Create a group for each state, with class state 
var gstates = svg.selectAll('g.state') 
    .data(states) 
    .enter() 
    .append('g') 
    .classed('state', true); 

// Add the state background, in this case, a transparent rectangle 
gstates 
    .append('rect') 
    .classed('state', true) 
    .attr('width', function(d) { return d.width; }) 
    .attr('height', function(d) { return d.height; }) 
    .attr('fill', '#444') 
    .attr('fill-opacity', 0.0); 

// For each group, add rectangles for each county, binding them to the 
// county array of each state. 
gstates.selectAll('rect.county') 
    .data(function(d) { return d.counties; }) 
    .enter() 
    .append('rect') 
    .classed('county', true) 
     .attr('x', function(d) { return d.x; }) 
     .attr('y', function(d) { return d.y; }) 
     .attr('width', function(d) { return d.width; }) 
     .attr('height', function(d) { return d.height; }) 
     .attr('fill', function(d) { return d.fill; }) 
     .on('mouseover', function() { 
      d3.select(this).attr('fill-opacity', 0.5); 
     }) 
     .on('mouseout', function() { 
      d3.select(this).attr('fill-opacity', 0.9); 
     }) 
     .on('click', function() { 
      // Retrive the parent group of each county, and then select 
      // the shape corresponding to the state and alter its properties 
      // you can also access the state data 
      var parent = d3.select(d3.select(this).node().parentNode), 
       state = parent.select('rect.state'); 
       // Output 'Fake State' 
       console.log(parent[0][0].__data__.name); 
       state.attr('fill-opacity', 1.0); 
     }); 

Jest skrzypce roboczego tutaj: http://jsfiddle.net/pnavarrc/PGTCM/5/. Pozdrawiam,

6

Oto funkcja d3, która robi to, co chcesz.

Przekazuje zdarzenia do niższych warstw, tymczasowo wyłączając zdarzenia wskaźnika na górnej warstwie i ręcznie wyzwalając zdarzenie myszy na następnej warstwie.

function passThruEvents(g) { 
    g .on('mousemove.passThru', passThru) 
     .on('mousedown.passThru', passThru) 
    ; 

    function passThru(d) { 
     var e = d3.event; 

     var prev = this.style.pointerEvents; 
     this.style.pointerEvents = 'none'; 

     var el = document.elementFromPoint(d3.event.x, d3.event.y); 

     var e2 = document.createEvent('MouseEvent'); 
     e2.initMouseEvent(e.type,e.bubbles,e.cancelable,e.view, e.detail,e.screenX,e.screenY,e.clientX,e.clientY,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,e.relatedTarget); 

     el.dispatchEvent(e2); 

     this.style.pointerEvents = prev; 
    } 
} 
+0

niesamowite rozwiązanie! – rockTheWorld

+1

To rozwiązanie jest niesamowite i łatwe do dostosowania do innych okoliczności, jednak elementy "zasłonięte" przez najwyższy element nie będą mogły być odtwarzane w stylu za pomocą ': hover', również chciałbym to naprawić. – luismreis

Powiązane problemy