SVG hover state on multiple layers, not only top l

2019-08-25 02:53发布

问题:

I want to display a decision tree as a SVG with somer interactivity. The different paths are all there and on hover have their opacity increased. But the hover state seems to apply only to the top element.

#flow polyline {
  stroke: #0071bc;
  stroke-opacity: 0;
  stroke-width: 8px;

  mix-blend-mode: color;
}
#flow polyline:hover {
  stroke-opacity: 1;
}

https://jsfiddle.net/yv82f3ud/

How can I apply the hover event on all hovered svg polylines, not only the top layer?


Followup question: Is there a way to have a wider path are on which the hover state is applied so I don't have to be exactly over the line?

回答1:

How can I apply the hover event on all hovered svg polylines, not only the top layer?

There is no trivial way. You would need to detect which section you are over, determine which ones should be highlighted, then manually highlight them yourself.

Is there a way to have a wider path are on which the hover state is applied so I don't have to be exactly over the line?

Yes, use a separate transparent thicker line

.line {
  stroke: grey;
  stroke-width: 2;
}

.line-wider {
  stroke: transparent;
  stroke-width: 40;
}

g:hover .line {
  stroke: blue;
}
<svg>
  <g>
    <path d="M0,50 L 300,50" class="line"/>
  </g>
  <g>
    <path d="M0,100 L 300,100" class="line"/>
    <path d="M0,100 L 300,100" class="line-wider"/>
  </g>
</svg>

You can use mouseover events on thick transparent line sections as a way to determine which section you are over, and do your highlighting based on that.

Something like the following:

// Add event listener to each "line-wider" path, that highlights the sections
var sections = document.querySelectorAll(".line-wider");
sections.forEach(function(elem) {
  elem.addEventListener("mouseover", doHover);
  elem.addEventListener("mouseout", clearHover);
});


var highlightedSections = {
  's1':   '.s1, .s11, .s12, .s111, .s112, .s121, .s122',
  's11':  '.s1, .s11, .s111, .s112',
  's12':  '.s1, .s12, .s121, .s122',
  's111': '.s1, .s11, .s111',
  's112': '.s1, .s11, .s112',
  's121': '.s1, .s12, .s121',
  's122': '.s1, .s12, .s122'
};

function doHover(evt) {
  // Which section are we hovering over?
  var id = evt.target.id;
  // highlightedSections[id] is a CSS selector which selects all the sections which should be highlighted for this hover section
  document.querySelectorAll(highlightedSections[id]).forEach( function(elem) {
    // Add the "highlight" class to all the matching sections
    elem.classList.add("highlight");
  });
}


// Remove the "highlight" class from all elements which currently have it set
function clearHover(evt) {
  document.querySelectorAll(".highlight").forEach( function(elem) {
    elem.classList.remove("highlight");
  });
}
.line {
  fill: none;
  stroke: grey;
  stroke-width: 2;
}

.line-wider {
  fill: none;
  stroke: transparent;
  stroke-width: 40;
}

.highlight {
  stroke: blue;
}
<svg width="400" height="500">
  <!-- top section -->
  <path d="M 200,0 L 200,100" class="line s1"/>
  <path d="M 200,0 L 200,100" class="line-wider" id="s1"/>

    <!-- left branch -->
    <path d="M 200,100 L 100,200, 100,300" class="line s11"/>
    <path d="M 200,100 L 100,200, 100,300" class="line-wider" id="s11"/>

      <!-- left branch -->
      <path d="M 100,300 L 50,350, 50,450" class="line s111"/>
      <path d="M 100,300 L 50,350, 50,450" class="line-wider" id="s111"/>
      <!-- right branch -->
      <path d="M 100,300 L 150,350, 150,450" class="line s112"/>
      <path d="M 100,300 L 150,350, 150,450" class="line-wider" id="s112"/>

    <!-- right branch -->
    <path d="M 200,100 L 300,200, 300,300" class="line s12"/>
    <path d="M 200,100 L 300,200, 300,300" class="line-wider" id="s12"/>

      <!-- left branch -->
      <path d="M 300,300 L 250,350, 250,450" class="line s121"/>
      <path d="M 300,300 L 250,350, 250,450" class="line-wider" id="s121"/>
      <!-- right branch -->
      <path d="M 300,300 L 350,350, 350,450" class="line s122"/>
      <path d="M 300,300 L 350,350, 350,450" class="line-wider" id="s122"/>

</svg>