1

I have a certain SVG, the contents of it is irrelevant for this question, however it is important to know that it contains the following structure:

<svg id='my-svg-id'>
  ...
  <path d="M138.331827,110.420944 ... 110.420944" id="Fill-80" fill="#07032E" />
  <path d="M139.331827,110.420944 ... 110.420944" id="Fill-80" fill="#07032E" />
  <path d="M140.331827,110.420944 ... 110.420944" id="Fill-80" fill="#07032E" />
  <path d="M141.331827,110.420944 ... 110.420944" id="Fill-80" fill="#07032E" />
  ...
</svg>

So it comes down to an svg tag, the parent, which contains several path tags, the children.

I want to animate these path's when hovered. More specific, my goal is to apply a css animation on the path element which is being hovered (:hover) and it's adjacent siblings +-5, if any.

So that would mean the hovered element itself + a maximum of 10 other elements would get targeted and therefore animated.

I've read some info regarding adjacent sibling combination, but can't seem to figure it out so far.

What I did manage to do however is target the hovered element and it's nearest sibling like this:

path:hover + path {
  animation: my-animation 0.5s linear infinite both;
}

Please provide answers or comment with any useful information. Although it would most welcome, I do not expect you to provide a fully working code example. Any useful information, in any form, is much appreciated.

isherwood
  • 58,414
  • 16
  • 114
  • 157
  • I think the issue is, if there are say 20 paths, then only 10 of them should be animated at any one time, and there's no way to have intersecting groups. – Peter Collingridge Sep 04 '19 at 12:13
  • 2
    I don't think this will be possible using just CSS, you'd have to use JS. – Peter Collingridge Sep 04 '19 at 12:15
  • 2
    Agreed, at least as long as the number of path siblings is unknown. If you had a fixed number of paths, it would be doable with a soup of :nth- and :not selectors. – Cody MacPixelface Sep 04 '19 at 12:18
  • 1
    @RobertLongson I don't think that would be a suitable solution for me. Since if I work with groups, e.g. of 5 paths, then it will always be those 5, or another group that get's animated. It is important for me to be able to animate the closest (physically on screen, the svg is ordered like this) paths, not always a select group if e.g. another path is closer but not from the same group. Hope that makes sense. – S. Van den Wyngaert Sep 04 '19 at 12:23
  • @PeterCollingridge That exactly is my point in my comment above, that indeed is a limitation, and one I have to work around somehow. – S. Van den Wyngaert Sep 04 '19 at 12:24
  • @Trollsyn well the number of path siblings is knows in this case. Thanks for the idea, I will play around with it. – S. Van den Wyngaert Sep 04 '19 at 12:25

3 Answers3

1

Selecting the next 5 elements

I did a quick try (really rough) and came up with the following: https://jsfiddle.net/dtwavyeq/4/

In order to affect the next 5 paths, I had to do the following:

path:hover + path {
  animation: example 0.1s linear infinite both;
}

path:hover + path + path {
  animation: example 0.1s linear infinite both;
}

path:hover + path + path + path {
  animation: example 0.1s linear infinite both;
}

path:hover + path + path + path + path {
  animation: example 0.1s linear infinite both;
}

path:hover + path + path + path + path + path {
  animation: example 0.1s linear infinite both;
}

Selecting previous elements

Unfortunately, there is no way to do this using CSS. According to the answer @ https://stackoverflow.com/a/1817801/1688441 .

Menelaos
  • 23,508
  • 18
  • 90
  • 155
1

Because there is only an following siblings combinator ('~'), but no preceding siblings combinator nor a general siblings combinator, we have to do a trick: We need the list of all the paths twice. The first one is the one that we react on hover and the second one are the adjacent paths, that will change color (and are initially hidden)

The adjacency must be introduced manually, as there is no such concept in CSS. I do this with a data-adjacent-to attribute

This is proof of concept and needs fine tuning:

.path {
  transition: color 1s;
  stroke: black;
  stroke-width: 1px;
  fill: currentcolor;
}
.path:hover {
  color: black !important;
}
.path.hide {
  pointer-events: none;
  opacity: 0;
  position: relative;
  z-index: 1;
  color: transparent;
}

#path-1:hover ~ [data-adjacent-to*="path-1"] {
  color: gray;
  opacity: 1;
}

#path-2:hover ~ [data-adjacent-to*="path-2"] {
  color: gray;
  opacity: 1;
}

#path-3:hover ~ [data-adjacent-to*="path-3"] {
  color: gray;
  opacity: 1;
}

#path-4:hover ~ [data-adjacent-to*="path-4"] {
  color: gray;
  opacity: 1;
}

#path-5:hover ~ [data-adjacent-to*="path-5"] {
  color: gray;
  opacity: 1;
}

#path-6:hover ~ [data-adjacent-to*="path-6"] {
  color: gray;
  opacity: 1;
}

#path-7:hover ~ [data-adjacent-to*="path-7"] {
  color: gray;
  opacity: 1;
}

#path-8:hover ~ [data-adjacent-to*="path-8"] {
  color: gray;
  opacity: 1;
}
<svg width="200" height="200" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
  <symbol id="lib-path-1" viewBox="0 0 100 100" >
    <path d="M50 50 l0 -50 l50 50"/>
  </symbol>
  <symbol id="lib-path-2" viewBox="0 0 100 100" >
    <path d="M50 50 l50 0 l-50 50"/>
  </symbol>
  <symbol id="lib-path-3" viewBox="0 0 100 100" >
    <path d="M50 50 l0 50 l-50 -50"/>
  </symbol>
  <symbol id="lib-path-4" viewBox="0 0 100 100" >
    <path d="M50 50 l-50 0 l50 -50"/>
  </symbol>
  <symbol id="lib-path-5" viewBox="0 0 100 100" >
    <path d="M100 0 l0 50 l-50 -50"/>
  </symbol>
  <symbol id="lib-path-6" viewBox="0 0 100 100" >
    <path d="M100 100 l-50 0 l50 -50"/>
  </symbol>
  <symbol id="lib-path-7" viewBox="0 0 100 100" >
    <path d="M0 100 l0 -50 l50 50"/>
  </symbol>
  <symbol id="lib-path-8" viewBox="0 0 100 100" >
    <path d="M0 0 l50 0 l-50 50"/>
  </symbol>
  
  <use class="path" id="path-1" xlink:href="#lib-path-1" style="color: green" />
  <use class="path" id="path-2" xlink:href="#lib-path-2" style="color: red" />
  <use class="path" id="path-3" xlink:href="#lib-path-3" style="color: blue" />
  <use class="path" id="path-4" xlink:href="#lib-path-4" style="color: orange" />
  <use class="path" id="path-5" xlink:href="#lib-path-5" style="color: pink" />
  <use class="path" id="path-6" xlink:href="#lib-path-6" style="color: silver" />
  <use class="path" id="path-7" xlink:href="#lib-path-7" style="color: gold" />
  <use class="path" id="path-8" xlink:href="#lib-path-8" style="color: brown" />
  <use class="path hide" xlink:href="#lib-path-1" data-adjacent-to="path-2 path-4 path-5"/>
  <use class="path hide" xlink:href="#lib-path-2" data-adjacent-to="path-1 path-3 path-6"/>
  <use class="path hide" xlink:href="#lib-path-3" data-adjacent-to="path-2 path-4 path-7"/>
  <use class="path hide" xlink:href="#lib-path-4" data-adjacent-to="path-1 path-3 path-8"/>
  <use class="path hide" xlink:href="#lib-path-5" data-adjacent-to="path-1"/>
  <use class="path hide" xlink:href="#lib-path-6" data-adjacent-to="path-2"/>
  <use class="path hide" xlink:href="#lib-path-7" data-adjacent-to="path-3"/>
  <use class="path hide" xlink:href="#lib-path-8" data-adjacent-to="path-4"/>
</svg>
yunzen
  • 32,854
  • 11
  • 73
  • 106
1

Yunzen's solution is a great solution using only CSS, but it does require you to edit your HTML/SVG code.

You could consider doing this with JavaScript, as it will result in much less code.

You can use the previousSibling and nextSibling properties of JS DOM Nodes:

var paths = document.querySelectorAll('path');
paths.forEach(e => {
  e.onmouseenter = function() {
    this.previousSibling.previousSibling.classList.add('hovered');
    this.previousSibling.classList.add('hovered');
    this.classList.add('hovered');
    this.nextSibling.classList.add('hovered');
    this.nextSibling.nextSibling.classList.add('hovered');
  }
  e.onmouseleave = function() {
    this.previousSibling.previousSibling.classList.remove('hovered');
    this.previousSibling.classList.remove('hovered');
    this.classList.remove('hovered');
    this.nextSibling.classList.remove('hovered');
    this.nextSibling.nextSibling.classList.remove('hovered');
  }
});

Ofcourse, you could come up with all sorts of ways to make this bit of code more readable, but hopefully you get the idea. Also, you should add checks to see if the previousSibling and nextSibling properties aren't null, right now the first 2 and last 2 paths will give errors.

And for the CSS:

.hovered {
   animation: my-animation 0.5s linear infinite both;
}