WEBLEB
Accueil
Éditeur
Connexion
Pro
Français
English
Français
Español
Português
Deutsch
Italiano
हिंदी
LECTEUR DE MUSIQUE
1361
ASKMUHAMMAD_2K
Ouvrir dans l'éditeur
Publiez votre code
Recommandé
19 May 2025
Montres connectées de luxe redéfinissant l'élégance et la technologie
17 May 2025
Formulaire de connexion moderne + barre latérale coulissante
16 May 2025
Un code de Maxi-Digital
HTML
Copy
Learn Basic String and Array Methods by Building a Music Player App
Music Player
Playlist
CSS
Copy
: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
Copy
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 shuffleButton = document.getElementById("shuffle"); 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(); let 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 || userData?.currentSong.id !== song.id) { audio.currentTime = 0; } else { audio.currentTime = userData?.songCurrentTime; } userData.currentSong = song; playButton.classList.add("playing"); highlightCurrentSong(); setPlayerDisplay(); setPlayButtonAccessibleText(); audio.play(); }; const pauseSong = () => { userData.songCurrentTime = audio.currentTime; playButton.classList.remove("playing"); audio.pause(); }; const playNextSong = () => { if (userData?.currentSong === null) { playSong(userData?.songs[0].id); } else { const currentSongIndex = getCurrentSongIndex(); const nextSong = userData?.songs[currentSongIndex + 1]; playSong(nextSong.id); } }; const playPreviousSong = () => { if (userData?.currentSong === null) return; else { const currentSongIndex = getCurrentSongIndex(); const previousSong = userData?.songs[currentSongIndex - 1]; playSong(previousSong.id); } }; const shuffle = () => { userData?.songs.sort(() => Math.random() - 0.5); userData.currentSong = null; userData.songCurrentTime = 0; renderSongs(userData?.songs); pauseSong(); setPlayerDisplay(); setPlayButtonAccessibleText(); }; const deleteSong = (id) => { if (userData?.currentSong?.id === id) { userData.currentSong = null; userData.songCurrentTime = 0; pauseSong(); setPlayerDisplay(); } userData.songs = userData?.songs.filter((song) => song.id !== id); renderSongs(userData?.songs); highlightCurrentSong(); setPlayButtonAccessibleText(); if (userData?.songs.length === 0) { const resetButton = document.createElement("button"); const resetText = document.createTextNode("Reset Playlist"); resetButton.id = "reset"; resetButton.ariaLabel = "Reset playlist"; resetButton.appendChild(resetText); playlistSongs.appendChild(resetButton); resetButton.addEventListener("click", () => { userData.songs = [...allSongs]; renderSongs(sortSongs()); setPlayButtonAccessibleText(); resetButton.remove(); }); } }; const setPlayerDisplay = () => { const playingSong = document.getElementById("player-song-title"); const songArtist = document.getElementById("player-song-artist"); const currentTitle = userData?.currentSong?.title; const currentArtist = userData?.currentSong?.artist; playingSong.textContent = currentTitle ? currentTitle : ""; songArtist.textContent = currentArtist ? currentArtist : ""; }; const highlightCurrentSong = () => { const playlistSongElements = document.querySelectorAll(".playlist-song"); const songToHighlight = document.getElementById( `song-${userData?.currentSong?.id}` ); playlistSongElements.forEach((songEl) => { songEl.removeAttribute("aria-current"); }); if (songToHighlight) songToHighlight.setAttribute("aria-current", "true"); }; const renderSongs = (array) => { const songsHTML = array .map((song)=> { return ` <li id="song-${song.id}" class="playlist-song"> <button class="playlist-song-info" onclick="playSong(${song.id})"> <span class="playlist-song-title">${song.title}</span> <span class="playlist-song-artist">${song.artist}</span> <span class="playlist-song-duration">${song.duration}</span> </button> <button onclick="deleteSong(${song.id})" class="playlist-song-delete" aria-label="Delete ${song.title}"> <svg width="20" height="20" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" r="8" fill="#4d4d62"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M5.32587 5.18571C5.7107 4.90301 6.28333 4.94814 6.60485 5.28651L8 6.75478L9.39515 5.28651C9.71667 4.94814 10.2893 4.90301 10.6741 5.18571C11.059 5.4684 11.1103 5.97188 10.7888 6.31026L9.1832 7.99999L10.7888 9.68974C11.1103 10.0281 11.059 10.5316 10.6741 10.8143C10.2893 11.097 9.71667 11.0519 9.39515 10.7135L8 9.24521L6.60485 10.7135C6.28333 11.0519 5.7107 11.097 5.32587 10.8143C4.94102 10.5316 4.88969 10.0281 5.21121 9.68974L6.8168 7.99999L5.21122 6.31026C4.8897 5.97188 4.94102 5.4684 5.32587 5.18571Z" fill="white"/></svg> </button> </li> `; }) .join(""); playlistSongs.innerHTML = songsHTML; }; const setPlayButtonAccessibleText = () => { const song = userData?.currentSong || userData?.songs[0]; playButton.setAttribute( "aria-label", song?.title ? `Play ${song.title}` : "Play" ); }; const getCurrentSongIndex = () => userData?.songs.indexOf(userData?.currentSong); playButton.addEventListener("click", () => { if (userData?.currentSong === null) { playSong(userData?.songs[0].id); } else { playSong(userData?.currentSong.id); } }); pauseButton.addEventListener("click", pauseSong); nextButton.addEventListener("click", playNextSong); previousButton.addEventListener("click", playPreviousSong); shuffleButton.addEventListener("click", shuffle); audio.addEventListener("ended", () => { const currentSongIndex = getCurrentSongIndex(); const nextSongExists = userData?.songs[currentSongIndex + 1] !== undefined; if (nextSongExists) { playNextSong(); } else { userData.currentSong = null; userData.songCurrentTime = 0; pauseSong() setPlayerDisplay() highlightCurrentSong() setPlayButtonAccessibleText() } }); const sortSongs = () => { userData?.songs.sort((a,b) => { if (a.title < b.title) { return -1; } if (a.title > b.title) { return 1; } return 0; }); return userData?.songs; }; renderSongs(sortSongs()); setPlayButtonAccessibleText();