1

I'm trying to write a JS function to change the color of multiple elements (including a SVG) when mouseover the parent tag (<a>). I tried to do with only CSS and the result isn't great.

const changeColor = () => {
 document.getElementsByClassName("link-label").style.color = '#3a4646';
  document.getElementById("Arrow").style.fill = '#3a4646';
  
  document.getElementByClassName("cls-1").style.stroke ='#3a4646';
  
  document.getElementByClassName("icon-arrow").style.border = "none";
}
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200&display=swap');

.container {
  height: 200px;
  display: grid;
  place-content: center;
  font-family: 'Poppins', sans-serif;
  background-color: #3a4646;
}

.btn--rounded {
  font-size: 20px;
    color: white;
    line-height: 1;
    letter-spacing: 0.4px;
    position: relative;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    padding: 12px 15px 12px 5px;
    transition: color 0.25s linear;
    text-decoration: none;
    background: transparent;
    border: none;
    z-index: 1;
}

.btn--rounded:before {
      width: 40px;
      height: 40px;
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      z-index:-1;
      animation-name: circleOut;
      animation-duration: 0.5s;
      animation-direction: alternate;
      animation-fill-mode: forwards;
}


.btn--rounded:hover:before {
          content: '';
          border: none;
          position: absolute;
          top: 50%;
          z-index: -1;
          animation-name: circleIn;
          animation-duration: 0.8s;
          animation-direction: alternate;
          animation-fill-mode: forwards;
}

.icon-arrow {
  border: 1px solid white;
  border-radius: 100%;
}


.link-label {
  padding-left: 20px;
}


@keyframes circleOut {
    0% {
      width: 100%;
      border-radius: 40px;
      background-color: #ffff;
    }
    90% {
      border-radius: 40px;
    }
    100% {
      width: 40px;
      border-radius: 100%;
      background-color: transparent;
    }
  }

@keyframes circleIn {
    0% {
      width: 60px;
      border-radius: 100%;
      background-color: transparent;
    }
    10% {
      border-radius: 60px;
    }
    100% {
      width: 100%;
      border-radius: 60px;
      background-color: #ffff;
    }
  }
<div class="container">
 
  <a href=# class="btn--rounded" onmouseover= "changeColor()">
      <div class="icon-arrow">
        <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36"><defs><style>.cls-1,.cls-2{fill:none}.cls-1{stroke:#fff}</style></defs><g id="Group_4802" transform="translate(-1113 -542)"><g id="Rectangle_2080" class="cls-1" transform="translate(1113 542)"></g><g id="Icon_Arrow_Base" transform="translate(1123 552)"><path id="Ligne" d="M15 0H0" class="cls-1" transform="translate(0 8)"/><path id="Arrow" fill="#fff" d="M12 0l-.727.727 2.753 2.753v1.039l-2.753 2.754L12 8l4-4z" transform="translate(0 4)"/></g></g></svg>
      </div>
      <div class="link-label">
      Subscribe!
      </div>
  </a>
<div>

But, now I don't know why my function isn't working... All the elements are undefined in the console. Anybody knows how to fix it?

analuspi
  • 184
  • 1
  • 1
  • 10
  • Does this answer your question? [What do querySelectorAll and getElementsBy\* methods return?](https://stackoverflow.com/questions/10693845/what-do-queryselectorall-and-getelementsby-methods-return) – Heretic Monkey Dec 15 '20 at 20:35

2 Answers2

0

getElementsByClassName() returns an array-like collection, not a single DOM element.

If you want to alter the first element returned:

document.getElementsByClassName("link-label")[0].style.color = '#3a4646';

Also, there is no getElementByClassName() function. So your changeColor() function should be:

const changeColor = () => {
  document.getElementsByClassName("link-label")[0].style.color = '#3a4646';
  document.getElementById("Arrow").style.fill = '#3a4646';
  document.getElementsByClassName("cls-1")[0].style.stroke ='#3a4646';
  document.getElementsByClassName("icon-arrow")[0].style.border = "none";
}
kmoser
  • 8,780
  • 3
  • 24
  • 40
  • Ok, that works, thanks. But now the initial button is gone (only the hover works). Do I need to add something to the tag to preserve the initial style? – analuspi Dec 15 '20 at 20:13
  • The `getElementsByClassName` issue is a frequent issue; so often is it asked that we have a canonical duplicate already set up for questions regarding it... – Heretic Monkey Dec 15 '20 at 20:37
  • It was missing the onmouseout! – analuspi Dec 15 '20 at 20:45
0

Why your code is not working:

When using getElementsByClassName(), you return a collection of HTML elements, think of it as an HTML list of all the elements that have a class with a given name, an array of values that are the classees elements. Even though you may only have one class in that list, Javascript is looking for further reference (index identifier) to tell which element within that list is being referenced.

Each element within that list must be identified using the index value. document.getElementsByClassName("link-label")[0], where zero in this example is the index identifier and the first iteration within that HTMLCollection list. This would be the static way to get the first child using the index in that HTMLCollection of elements that have that class name.

You could also use the selector -> document.querySelector(".class_name") -> returns the first Element within the document that matches the specified selector. When you use querySelector(), there is no need to use the index identifier.

Note that you have more than one class with .cls-1, you must create a loop for these elements that handles each element.

You also need a onmouseout event that changes those colors and borders back to the they were before. The animation @keyframes will need to by tweaked to get a transformation on the onmouseback event.

See the snippit below:

const changeColor = () => {
  document.querySelector(".link-label").style.color = '#3a4646';
  document.getElementById("Arrow").style.fill = '#3a4646';

  // because cls1 has more that one element within is HTML Collection
  // we must loop over the collection and style each element using
  // a index identifier within a loop using a basic for loop and iterator

  let cls1 = document.querySelectorAll(".cls-1");
  for (let i = 0; i < cls1.length; i++) {
    cls1[i].style.stroke = '#3a4646';
  }
  document.querySelector(".icon-arrow").style.border = "none";
}



const changeBack = () => {
  document.querySelector(".link-label").style.color = '#eee';
  document.getElementById("Arrow").style.fill = '#eee';

  // because cls1 has more that one element within is HTML Collection
  // we must loop over the collection and style each element using
  // a index identifier within a loop using a basic for loop and iterator

  let cls1 = document.querySelectorAll(".cls-1");
  for (let i = 0; i < cls1.length; i++) {
    cls1[i].style.stroke = '#eee';
  }
  document.querySelector(".icon-arrow").style.border = "1px solid white";
}
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200&display=swap');
.container {
  height: 200px;
  display: grid;
  place-content: center;
  font-family: 'Poppins', sans-serif;
  background-color: #3a4646;
}

.btn--rounded {
  font-size: 20px;
  color: white;
  line-height: 1;
  letter-spacing: 0.4px;
  position: relative;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  padding: 12px 15px 12px 5px;
  transition: color 0.25s linear;
  text-decoration: none;
  background: transparent;
  border: none;
  z-index: 1;
}

.btn--rounded:before {
  width: 40px;
  height: 40px;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: -1;
  animation-name: circleOut;
  animation-duration: 0.5s;
  animation-direction: alternate;
  animation-fill-mode: forwards;
  color: #eee;
}

.btn--rounded:hover:before {
  content: '';
  border: none;
  position: absolute;
  top: 50%;
  z-index: -1;
  animation-name: circleIn;
  animation-duration: 0.8s;
  animation-direction: alternate;
  animation-fill-mode: forwards;
}

.icon-arrow {
  border: 1px solid white;
  border-radius: 100%;
}

.link-label {
  padding-left: 20px;
}

@keyframes circleOut {
  0% {
    width: 100%;
    border-radius: 40px;
    background-color: #ffff;
  }
  90% {
    border-radius: 40px;
  }
  100% {
    width: 40px;
    border-radius: 100%;
    background-color: transparent;
  }
}

@keyframes circleIn {
  0% {
    width: 60px;
    border-radius: 100%;
    background-color: transparent;
  }
  10% {
    border-radius: 60px;
  }
  100% {
    width: 100%;
    border-radius: 60px;
    background-color: #ffff;
  }
}
<div class="container">

  <a href=# class="btn--rounded" onmouseover="changeColor()" onmouseout="changeBack()">
    <div class="icon-arrow">
      <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36">
        <defs>
          <style>.cls-1,.cls-2{fill:none}.cls-1{stroke:#fff}</style>
        </defs>
        <g id="Group_4802" transform="translate(-1113 -542)">
        <g id="Rectangle_2080" class="cls-1" transform="translate(1113 542)"></g>
        <g id="Icon_Arrow_Base" transform="translate(1123 552)">
        <path id="Ligne" d="M15 0H0" class="cls-1" transform="translate(0 8)"/>
        <path id="Arrow" fill="#fff" d="M12 0l-.727.727 2.753 2.753v1.039l-2.753 2.754L12 8l4-4z" transform="translate(0 4)"/>
        </g>
        </g>
        </svg>
    </div>
    <div class="link-label">
      Subscribe!
    </div>
  </a>
  <div>
dale landry
  • 7,831
  • 2
  • 16
  • 28