Removing a class, then adding it back results in a different display

Viewed 109

I have a certain css styled class called "line". On a button click (PLAY button) I remove this class from my element "song_info" then add a new class called "standard". On another button click (PAUSE button) I remove the class "standard" then re-add the class "line".

function play_button() {
  document.getElementById('song_info').classList.remove("line");
  document.getElementById('song_info').classList.add("standard");
}

function pause_button() {
  document.getElementById('song_info').classList.remove("standard");
  document.getElementById('song_info').classList.add("line");
}


window.onload = function() {
  document.getElementById("play_button").onclick = play_button;
  document.getElementById("pause_button").onclick = pause_button;
}
.css3gradient {
  width: 298px;
  height: 180px;
}

body {
  margin: 10px;
  white-space: nowrap;
}

#play_button {
  top: 65%;
  left: 113px;
  position: absolute;
  transform: translate(-50%, -50%);
}

#pause_button {
  top: 65%;
  left: 191px;
  position: absolute;
  transform: translate(-50%, -50%);
}

#songContainer {
  width: 198px;
  height: 50px;
  position: absolute;
  overflow: hidden;
  padding: 0;
  margin: 0;
  top: 50px;
  left: 60px;
  z-index: -2;
}

#albumContainer {
  width: 198px;
  height: 50px;
  position: absolute;
  overflow: hidden;
  padding: 0;
  top: 72px;
  left: 60px;
  /*z-index: -1;*/
}

#songWrapper {
  position: absolute;
  overflow: hidden;
  width: auto;
  height: 100%;
  top: 0;
  left: 0;
  z-index: -2;
}

#albumWrapper {
  position: absolute;
  overflow: hidden;
  width: auto;
  height: 100%;
  top: 0;
  left: 0;
  /*z-index: -1;*/
}

#songWrapper span.line {
  position: absolute;
  overflow: hidden;
  position: relative;
  /*margin: 15px;*/
  width: auto;
  top: 35%;
  left: 50%;
  animation: marquee 10s linear infinite;
  z-index: -2;
}

#albumWrapper span.line {
  position: absolute;
  overflow: hidden;
  position: relative;
  width: auto;
  top: 50%;
  left: 50%;
  animation: marquee 10s linear infinite;
  z-index: -1;
}

#songWrapper span.standard {
  margin: 15px;
  visibility: visible;
  display: block;
  position: relative;
  width: 149px;
  height: 45px;
  top: 50%;
  left: 50%;
  z-index: -1;
  /*
  margin-top: 11px;
  width: 200px;
  visibility: visible;
  position: absolute;
  top: 20px;
  left 20px;
  */
  transform: translate(-50%, -50%);
}

#albumWrapper span.standard {
  margin: 15px;
  visibility: visible;
  display: block;
  position: relative;
  width: 149px;
  height: 45px;
  top: 50%;
  left: 50%;
  z-index: -1;
  /*
  margin-top: 11px;
  width: 200px;
  visibility: visible;
  position: absolute;
  top: 20px;
  left 20px;
  */
  transform: translate(-50%, -50%);
}

@keyframes marquee {
  0% {
    left: -17%;
  }
  100% {
    left: -50.4%;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="css3gradient">
  <div class="idiv">
    <div id="songContainer" class="marqueeContainer">
      <div id="songWrapper" class="loaded">
        <span id="song_info" class="line shadowedObj">No Song Is Currently Playing!!!&nbsp;&nbsp;&nbsp;No Song Is Currently Playing!!!&nbsp;&nbsp;&nbsp;No Song Is Currently Playing!!!</span>
      </div>
    </div>
    <div id="albumContainer" class="marqueeContainer">
      <div id="albumWrapper" class="loaded">
        <span id="album_info" class="line shadowedObj">Album is longer and long enough&nbsp;&nbsp;&nbsp;Album is longer and long enough&nbsp;&nbsp;&nbsp;Album is longer and long enough</span>
      </div>
    </div>
  </div>
  <div class="idiv">
    <button class="button" id="play_button">PLAY</button>
    <button class="button" id="pause_button">PAUSE</button>
  </div>
</div>

The expected behavior is that the css styling would return to normal. However this is not the case and the position is raised rather significantly. Wouldn't we always expect the styling to be the same when a given class is removed and then re-added? What exactly is going on that it has different styling? What can be done to achieve the intended effect and revert css changes via adding and removing a class?

An example can be found here: JSFiddle

1 Answers

I think you've found a bug in Blink that it inherited from WebCore in WebKit (Blink started as a fork of WebCore). It's probably worth reporting it to the WebKit project and perhaps also to the Chromium project that Blink is part of.

I base this assessment on:

  • The same classes really should result in the same display.
  • The problem happens on browsers like Brave, Chrome, Chromium-based Edge, and Safari (WebKit), and does not happen on Firefox (which uses Gecko) or Legacy Edge (which uses EdgeHTML)

Here's a workaround created by Anakin, see comments in the code:

function play_button() {
  document.getElementById('song_info').classList.remove("line");
  document.getElementById('song_info').classList.add("standard");
}

function pause_button() {
  document.getElementById('song_info').classList.remove("standard");
  document.getElementById('song_info').classList.add("line");
}


window.onload = function() {
  document.getElementById("play_button").onclick = play_button;
  document.getElementById("pause_button").onclick = pause_button;
}
.css3gradient {
  width: 298px;
  height: 180px;
}

body {
  margin: 10px;
  white-space: nowrap;
}

#play_button {
  top: 65%;
  left: 113px;
  position: absolute;
  transform: translate(-50%, -50%);
}

#pause_button {
  top: 65%;
  left: 191px;
  position: absolute;
  transform: translate(-50%, -50%);
}

#albumContainer {
  width: 198px;
  height: 50px;
  position: absolute;
  overflow: hidden;
  padding: 0;
  top: 72px;
  left: 60px;
  //z-index: -1;
}

#songWrapper {
  position: absolute;
  overflow: hidden;
  width: auto;
  height: 100%;
  top: 0;
  left: 0;
  z-index: -2;
}

#albumWrapper {
  position: absolute;
  overflow: hidden;
  width: auto;
  height: 100%;
  top: 0;
  left: 0;
}

#albumWrapper span.line {
  overflow: hidden;
  position: relative;
  width: auto;
  top: 50%;
  left: 50%;
  animation: marquee 10s linear infinite;
  z-index: -1;
}

#albumWrapper span.standard {
  margin: 15px;
  visibility: visible;
  display: block;
  position: relative;
  width: 149px;
  height: 45px;
  top: 50%;
  left: 50%;
  z-index: -1;
  transform: translate(-50%, -50%);
}

@keyframes marquee {
  0% {
    left: -17%;
  }
  100% {
    left: -50.4%;
  }
}

#songWrapper span.line {
  /* removed top and right property */
  position: relative;
  width: auto;
  overflow: hidden;
  animation: marquee 10s linear infinite;
  z-index: -2;
}

#songWrapper span.standard {
  /* there is no need to change into a block element
     so, what's really important in this class is removing the animation */
  animation: none;
}

#songContainer {
  /* now we move this element down to make it near with #albumContainer */
  position: absolute;
  overflow: hidden;
  width: 198px;
  height: 30px;
  /* decrease the height */
  padding: 0;
  margin: 0;
  top: 72px;
  /* move this down a little more */
  left: 60px;
  z-index: -2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="css3gradient">
  <div class="idiv">
    <div id="songContainer" class="marqueeContainer">
      <div id="songWrapper" class="loaded">
        <span id="song_info" class="line shadowedObj">No Song Is Currently Playing!!!&nbsp;&nbsp;&nbsp;No Song Is Currently Playing!!!&nbsp;&nbsp;&nbsp;No Song Is Currently Playing!!!</span>
      </div>
    </div>
    <div id="albumContainer" class="marqueeContainer">
      <div id="albumWrapper" class="loaded">
        <span id="album_info" class="line shadowedObj">Album is longer and long enough&nbsp;&nbsp;&nbsp;Album is longer and long enough&nbsp;&nbsp;&nbsp;Album is longer and long enough</span>
      </div>
    </div>
  </div>
  <div class="idiv">
    <button class="button" id="play_button">PLAY</button>
    <button class="button" id="pause_button">PAUSE</button>
  </div>
</div>

That uses a div class="css3gradient" as its outermost element (because the Snippet itself provides body), but it works just fine when you're using body class="css3gradient" as the outermost instead.

Related