Back to Freecodecamp

Step 27

curriculum/challenges/english/blocks/workshop-music-player/674f534fa181f64a789ffcf9.md

latest18.9 KB
Original Source

--description--

If userData.currentSong is not null you need to find the next song to play. Remember that you can use getNextSong for that.

Complete your playNextSong function so that it plays the next song in the playlist. If the current song is the last one, set userData.currentSong to null, userData.songCurrentTime to zero and call the pauseSong function.

--hints--

When the currentSong of userData is truthy and not the last song of the playlist, the playNextSong function should play the song next to the current song.

js
let flag;
const testArr = [];
const temp1 = userData.currentSong;
userData.currentSong = allSongs[3];
const temp2 = playSong;
playSong = id => {
  flag = true;
  testArr.push(id);
}
try {
  playNextSong();
  assert.isTrue(flag);
  userData.currentSong = allSongs[4];
  playNextSong();
  assert.deepEqual(testArr, [4, 5]);
} finally {
  userData.currentSong = temp1;
  playSong = temp2;
}

When the currentSong of userData is the last song of the playlist, the playNextSong function should set userData.currentSong to null.

js
const temp = userData.currentSong;
userData.currentSong = allSongs[9];
try {  
  playNextSong();
  assert.isNull(userData.currentSong);
} finally {
  userData.currentSong = temp;
}

When the currentSong of userData is the last song of the playlist, the playNextSong function should set userData.songCurrentTime to 0.

js
const temp = userData.songCurrentTime;
userData.songCurrentTime = 10;
try {
  userData.currentSong = allSongs[9];
  playNextSong();
  assert.strictEqual(userData.songCurrentTime, 0);
} finally {
  userData.songCurrentTime = temp;
}

When the currentSong of userData is the last song of the playlist, the playNextSong function should call the pauseSong function.

js
let flag;
const temp = pauseSong;
pauseSong = () => {flag = true};
try {
  userData.currentSong = allSongs[9];
  playNextSong();
  assert.isTrue(flag);
} finally {
  pauseSong = temp;
}

When the currentSong of userData is equal to null, your playNextSong function should still call the playSong function with the id of the first song in the userData.songs array as an argument.

js
let flag;
const testArr = [];
const temp = playSong;
playSong = id => {
  flag = true;
  testArr.push(id);
}
try {
  userData.currentSong = null;
  playNextSong();
  assert.isTrue(flag);
  assert.deepEqual(testArr, [0]);
} finally {
  playSong = temp;
}

--seed--

--seed-contents--

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap" rel="stylesheet" />
  <link href="https://fonts.googleapis.com/css2?family=Lato&family=Roboto+Mono&display=swap" rel="stylesheet" />
  <link rel="stylesheet" href="styles.css" />
  <title>
    Build a Music Player App
  </title>
</head>

<body>
  <div class="container">
    <div class="player">
      <div class="player-bar">
        <div class="parallel-lines">
          <div></div>
          <div></div>
        </div>
        <h1 class="fcc-title">freeCodeCamp</h1>
        <div class="parallel-lines">
          <div></div>
          <div></div>
        </div>
      </div>
      <div class="player-content">
        <div id="player-album-art">
          
        </div>
        <div class="player-display">
          <div class="player-display-song-artist">
            <p id="player-song-title"></p>
            <p id="player-song-artist"></p>
          </div>
          <div class="player-buttons">
            <button id="previous" class="previous" aria-label="Previous">
              <svg width="24" height="19" viewBox="0 0 24 19" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M23.2248 0L7.03964 9.5L23.2248 19L23.2248 0Z" />
                <rect width="4.63633" height="18.5453" transform="matrix(-1 0 0 1 4.63633 0)" />
              </svg>
            </button>
            <button id="play" class="play" aria-label="Play">
              <svg width="17" height="19" viewBox="0 0 17 19" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M0 0L16.1852 9.5L1.88952e-07 19L0 0Z" />
              </svg>
            </button>
            <button id="pause" class="pause" aria-label="Pause">
              <svg width="17" height="19" viewBox="0 0 17 19" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M0 6.54013e-07H4.75V19H0V6.54013e-07Z" />
                <path d="M11.4 0H16.15V19H11.4V0Z" />
              </svg>
            </button>
            <button id="next" class="next" aria-label="Next">
              <svg width="24" height="19" viewBox="0 0 24 19" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M0 0L16.1852 9.5L1.88952e-07 19L0 0Z" />
                <rect x="18.5885" width="4.63633" height="18.5453" />
              </svg>
            </button>
          </div>
        </div>
      </div>
    </div>
    <div class="playlist">
      <div class="playlist-bar">
        <div class="parallel-lines">
          <div></div>
          <div></div>
        </div>
        <h2 class="playlist-title" id="playlist">Playlist</h2>
        <div class="parallel-lines">
          <div></div>
          <div></div>
        </div>
      </div>
      <ul id="playlist-songs">
        <li id="song-0" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Scratching The Surface</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">4:25</span>
          </button>
        </li>

        <li id="song-1" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Can't Stay Down</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">4:15</span>
          </button>
        </li>

        <li id="song-2" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Still Learning</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">3:51</span>
          </button>
        </li>

        <li id="song-3" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Cruising for a Musing</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">3:34</span>
          </button>
        </li>

        <li id="song-4" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Never Not Favored</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">3:35</span>
          </button>
        </li>

        <li id="song-5" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">From the Ground Up</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">3:12</span>
          </button>
        </li>

        <li id="song-6" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Walking on Air</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">3:25</span>
          </button>
        </li>

        <li id="song-7" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Can't Stop Me. Can't Even Slow Me Down.</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">3:52</span>
          </button>
        </li>

        <li id="song-8" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">The Surest Way Out is Through</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">3:10</span>
          </button>
        </li>

        <li id="song-9" class="playlist-song">
          <button class="playlist-song-info" >
            <span class="playlist-song-title">Chasing That Feeling</span>
            <span class="playlist-song-artist">Quincy Larson</span>
            <span class="playlist-song-duration">2:43</span>
          </button>
        </li>
      </ul>
    </div>
  </div>
  <script src="script.js"></script>
</body>

</html>
css
:root {
    /* colors */
    --primary-color: #dfdfe2;
    --secondary-color: #ffffff;
    --app-background-color: #4d4d62;
    --background-color: #1b1b32;
    --foreground-color: #3b3b4f;
    --highlight-color: #f1be32;

    /* font sizes */
    --root-font-size: 16px;
    font-size: var(--root-font-size);

    /* font-families */
    --font-headline: "Roboto Mono", monospace;
    --font-family: "Lato", sans-serif;
  }

  *,
  *::after,
  *::before {
    box-sizing: border-box;
  }

  body {
    background-color: var(--app-background-color);
    color: var(--primary-color);
    font-family: var(--font-family);
  }

  h1 {
    font-size: 1.125rem;
    line-height: 1.6;
  }

  h2 {
    font-size: var(--root-font-size);
  }

  ul {
    margin: 0;
  }

  .container {
    margin-top: 10px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    row-gap: 5px;
  }

  .player,
  .playlist {
    width: 450px;
    background-color: var(--background-color);
    border: 3px solid var(--foreground-color);
  }

  .player {
    height: 260px;
    padding: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    row-gap: 10px;
  }

  .player-bar,
  .playlist-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 5px;
    width: 100%;
    height: 30px;
    background-color: var(--foreground-color);
  }

  .parallel-lines {
    display: flex;
    flex-wrap: wrap;
    row-gap: 6px;
    padding: 0 5px;
  }

  .parallel-lines > div {
    height: 2px;
    width: 100%;
    min-width: 75px;
    background-color: var(--highlight-color);
  }

  .fcc-title,
  .playlist-title {
    color: var(--secondary-color);
    margin: 0 10px;
    font-family: var(--font-headline);
  }

  .player-content {
    display: flex;
    background-color: var(--foreground-color);
    width: 430px;
    height: 200px;
    column-gap: 13px;
    align-items: center;
    justify-content: center;
  }

  #player-album-art {
    background-color: var(--secondary-color);
    border: 6px solid var(--background-color);
  }

  #player-album-art img {
    width: 150px;
    display: block;
  }

  .player-display {
    display: flex;
    flex-direction: column;
    row-gap: 20px;
    padding: 14px;
    background-color: var(--background-color);
    height: 153px;
    width: 226px;
  }

  .player-display-song-artist {
    height: 80px;
  }

  .player-buttons svg {
    fill: var(--primary-color);
  }

  .playing > svg {
    fill: var(--highlight-color);
  }

  .player-buttons {
    display: flex;
    justify-content: space-around;
  }

  button {
    background: transparent;
    border: none;
    color: var(--primary-color);
    cursor: pointer;
    font-size: var(--root-font-size);
    outline-color: var(--highlight-color);
    text-align: center;
  }

  .playlist-song {
    outline-color: var(--highlight-color);
  }

  .playlist li:not(:last-child) {
    border-bottom: 1px solid var(--background-color);
  }

  button:focus,
  .playlist-song:focus {
    outline-style: dashed;
    outline-width: 2px;
  }

  /* Playlist */
  .playlist {
    height: auto;
    padding: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    row-gap: 10px;
  }

  #playlist-songs {
    width: 430px;
    height: 100%;
    background-color: var(--foreground-color);
    display: flex;
    flex-direction: column;
    row-gap: 8px;
    padding: 8px 9px;
    visibility: visible;
    justify-content: start;
    list-style: none;
  }

  .playlist-song {
    display: flex;
    height: 55px;
    justify-content: space-between;
    align-items: center;
    padding: 5px;
  }

  [aria-current="true"] {
    background-color: var(--background-color);
  }

  [aria-current="true"] p {
    color: var(--highlight-color);
  }

  .playlist-song-info {
    height: 100%;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-around;
    column-gap: 7px;
    padding: 5px 0;
    font-family: var(--font-family);
  }

  #player-song-title,
  #player-song-artist {
    margin: 0;
  }

  #player-song-artist {
    color: var(--highlight-color);
    font-size: 0.75rem;
  }

  #player-song-title {
    font-size: 1.125rem;
  }

  .playlist-song-title {
    font-size: 0.85rem;
    width: 241px;
    text-align: left;
  }

  .playlist-song-artist {
    font-size: 0.725rem;
    width: 80px;
  }

  .playlist-song-duration {
    font-size: 0.725rem;
    margin: auto;
    font-family: var(--font-headline);
    width: 30px;
  }

  .playlist-song-delete {
    padding: 0;
    width: 20px;
    height: 20px;
  }

  .playlist-song-delete,
  .playlist-song-delete {
    fill: var(--foreground-color);
  }

  .playlist-song-delete:hover circle,
  .playlist-song-delete:focus circle {
    fill: #ff0000;
  }

  @media (max-width: 700px) {
    .player,
    .playlist {
      width: 300px;
    }

    .player {
      height: 340px;
    }

    #playlist-songs {
      height: 280px;
      padding: 5px 6px;
      overflow-y: scroll;
      overflow-x: hidden;
      scrollbar-color: var(--background-color) var(--secondary-color);
      scrollbar-width: thin;
    }

    #playlist-songs::-webkit-scrollbar {
      width: 5px;
    }

    #playlist-songs::-webkit-scrollbar-track {
      background: var(--background-color);
    }

    #playlist-songs::-webkit-scrollbar-thumb {
      background: var(--secondary-color);
    }

    h1 {
      font-size: 0.813rem;
    }

    h2 {
      font-size: 0.75rem;
    }

    .player-bar,
    .playlist-bar,
    .player-content,
    #playlist-songs {
      width: 280px;
    }

    .playlist-song {
      justify-content: space-between;
    }

    .playlist-song-title {
      width: 140px;
    }

    .playlist-song-artist {
      width: 40px;
    }

    .playlist-song-duration > button {
      padding: 0;
    }

    .player-content {
      display: inline;
      position: relative;
      justify-items: center;
      height: 100%;
    }

    #player-album-art {
      z-index: -100;
      height: 280px;
      box-shadow: none;
      background: #000;
    }

    #player-album-art img {
      width: 100%;
      opacity: 0.6;
    }

    .player-display-song-artist {
      padding: 0 10px;
    }

    .player-display-song-artist > p {
      white-space: pre-wrap;
    }

    .player-display {
      position: absolute;
      width: 100%;
      z-index: 1000;
      background-color: transparent;
      top: 0;
      height: 280px;
      justify-content: space-between;
      text-align: center;
    }
  }
js
const playlistSongs = document.getElementById("playlist-songs");
const playButton = document.getElementById("play");
const pauseButton = document.getElementById("pause");
const nextButton = document.getElementById("next");
const previousButton = document.getElementById("previous");
const allSongs = [
  {
    id: 0,
    title: "Scratching The Surface",
    artist: "Quincy Larson",
    duration: "4:25",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/scratching-the-surface.mp3",
  },
  {
    id: 1,
    title: "Can't Stay Down",
    artist: "Quincy Larson",
    duration: "4:15",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/can't-stay-down.mp3",
  },
  {
    id: 2,
    title: "Still Learning",
    artist: "Quincy Larson",
    duration: "3:51",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/still-learning.mp3",
  },
  {
    id: 3,
    title: "Cruising for a Musing",
    artist: "Quincy Larson",
    duration: "3:34",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/cruising-for-a-musing.mp3",
  },
  {
    id: 4,
    title: "Never Not Favored",
    artist: "Quincy Larson",
    duration: "3:35",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/never-not-favored.mp3",
  },
  {
    id: 5,
    title: "From the Ground Up",
    artist: "Quincy Larson",
    duration: "3:12",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/from-the-ground-up.mp3",
  },
  {
    id: 6,
    title: "Walking on Air",
    artist: "Quincy Larson",
    duration: "3:25",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/walking-on-air.mp3",
  },
  {
    id: 7,
    title: "Can't Stop Me. Can't Even Slow Me Down.",
    artist: "Quincy Larson",
    duration: "3:52",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/cant-stop-me-cant-even-slow-me-down.mp3",
  },
  {
    id: 8,
    title: "The Surest Way Out is Through",
    artist: "Quincy Larson",
    duration: "3:10",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/the-surest-way-out-is-through.mp3",
  },
  {
    id: 9,
    title: "Chasing That Feeling",
    artist: "Quincy Larson",
    duration: "2:43",
    src: "https://cdn.freecodecamp.org/curriculum/js-music-player/chasing-that-feeling.mp3",
  },
];

const audio = new Audio();

const userData = {
  songs: allSongs,
  currentSong: null,
  songCurrentTime: 0,
}

const playSong = id => {
  const song = userData.songs.find((song) => song.id === id);
  audio.src = song.src;
  audio.title = song.title;
  if (userData.currentSong === null) {
    audio.currentTime = 0
  } else {
    audio.currentTime = userData.songCurrentTime;
  }
  userData.currentSong = song;
  playButton.classList.add("playing");
  audio.play()
}

const pauseSong = () => {
  userData.songCurrentTime = audio.currentTime;
  playButton.classList.remove("playing");
  audio.pause();
}

const getCurrentSongIndex = () => userData.songs.indexOf(userData.currentSong);

const getNextSong = () => userData.songs[getCurrentSongIndex() + 1];

--fcc-editable-region--
const playNextSong = () => {
  if (userData.currentSong === null) {
    playSong(userData.songs[0].id);
  }

}
--fcc-editable-region--


playButton.addEventListener("click", () => {
  if (userData.currentSong === null) {
    playSong(userData.songs[0].id);
  } else {
    playSong(userData.currentSong.id);
  }
});

const songs = document.querySelectorAll(".playlist-song");

songs.forEach((song) => {
  const id = song.getAttribute("id").slice(5);
  const songBtn = song.querySelector("button");
  songBtn.addEventListener("click", () => {
      playSong(Number(id));
  })
})

pauseButton.addEventListener("click", pauseSong);