Search code examples
javascriptjqueryhtmlappendhtmlcollection

HTMLCollection Index Change Not Reflected in Variables After .append()


I have a stack of divs that I have set up in a carousel fashion. Using JQuery I .append() or .prepend() a div to the top or bottom of the stack when "next" or "previous" buttons are clicked. The top/visible div is the "active" div that corresponds to an <iframe> of a similarly named id attribute. When a div becomes "active" after clicking a next/previous button, I want my Javascript to pull the src attribute value from the corresponding <iframe> so I can use it elsewhere in my program.

The problem I'm having is that when I click the previous or next buttons, the divs reorder perfectly, but the corresponding variables do not reflect the new "active" div. For example, activeAlbumID remains discovery-sounds when it should be space-sounds after a button click.

Here's my <html>:

<div id='stack'>
  <div class="album" id="space-sounds"></div>
  <div class="album" id="vintage-sounds"></div>
  <div class="album" id="rocket-sounds"></div>
  <div class="album" id="mercury-sounds"></div>
  <div class="album" id="kennedy-sounds"></div>
  <div class="album" id="apollo-sounds"></div>
  <div class="album" id="shuttle-sounds"></div>
  <div class="album" id="discovery-sounds"></div>
</div>

<iframe id="space-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/558303876"></iframe>
<iframe id="vintage-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/270811947"></iframe>
<iframe id="rocket-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/55326279"></iframe>
<iframe id="mercury-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/55323699"></iframe>
<iframe id="kennedy-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/55323646"></iframe>
<iframe id="apollo-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/55323442"></iframe>
<iframe id="shuttle-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/55323280"></iframe>
<iframe id="discovery-widget" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/55322828"></iframe>

Here's the Javascript:

$(window).bind("load", function() {

  var albums = document.getElementsByClassName('album');
  var playlists = document.getElementsByTagName('iframe');
  var activeAlbum = albums[albums.length - 1];
  var activeAlbumID = activeAlbum.id;
  var activeAlbumPre = activeAlbumID.split('-');
  var activePlaylist = activeAlbumPre[0] + '-widget';
  var widgetSRC = $('#' + activePlaylist).attr('src');
  
  $('i:nth-child(1), i:nth-child(2)').click(function(){
    //Click previous button
    if (this.id == 'prev'){
      //Pull out album from bottom of stack
      $(albums[0]).addClass('animate-album');
      //Wait for animation to finish
      $(albums[0]).one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) {
        //Set explicit element margin
        $(albums[0]).css('margin-left', '400px');
        //Remove animation styles/margin so doesn't re-fire on append
        $(albums[0]).removeClass('animate-album');
        //Put bottom album on top
        $('#stack').append(albums[0]);
        //Force browser to calculate margin value
        $(albums[albums.length - 1]).css('margin-left');
        //Transition album back to original position
        $(albums[albums.length - 1]).css('margin-left', '0px');
      });
    }
    else{
      //Push album to bottom of stack
      $(albums[albums.length - 1]).addClass('animate-album');
      //Wait for animation to finish
      $(albums[albums.length - 1]).one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) {
        //Set explicit element margin
        $(albums[albums.length - 1]).css('margin-left', '400px');
        //Remove animation styles/margin so doesn't re-fire on prepend
        $(albums[albums.length - 1]).removeClass('animate-album');
        //Put top album on bottom
        $('#stack').prepend($(albums[albums.length - 1]));
        //Force browser to calculate margin value
        $(albums[0]).css('margin-left');
        //Transition album back to original position
        $(albums[0]).css('margin-left', '0px');
      });
    }
  });
 });

So for instance, when I click the "previous" button, discovery-sounds gets pushed to the bottom of the stack and space-sounds to the top and becomes visible, this works great:

<div id='stack'>
  <div class="album" id="discovery-sounds"></div>
  <div class="album" id="vintage-sounds"></div>
  <div class="album" id="rocket-sounds"></div>
  <div class="album" id="mercury-sounds"></div>
  <div class="album" id="kennedy-sounds"></div>
  <div class="album" id="apollo-sounds"></div>
  <div class="album" id="shuttle-sounds"></div>
  <div class="album" id="space-sounds"></div>
</div>

Printing albums to the console produces the updated and correct stack index, but all variables following produce the original "active" value:

//Expected
console.log(activeAlbum); //<div class="album" id="space-sounds"></div>
console.log(activeAlbumID); //spsce-sounds

//Actual
console.log(activeAlbum); //<div class="album" id="discovery-sounds"></div>
console.log(activeAlbumID); //discovery-sounds


Solution

  • Just before the end of the animationend handlers, just "renew" the elements collection that "move"... Due to the append/prepend in DOM.

    // Renew the albums collection.
    albums = document.getElementsByClassName('album');
    activeAlbum = albums[albums.length - 1];
    activeAlbumID = activeAlbum.id;
    

    Updated forked CodePen