【问题标题】:playPause button not resetting upon track skip跳过曲目时播放暂停按钮未重置
【发布时间】:2020-05-05 11:25:25
【问题描述】:

所以这是我第一次使用 JavaScript,我遇到了一些播放器的错误。除了一些 CSS 问题,目前只有两件事不起作用。首先,这篇文章的主要问题是当我换歌时播放/暂停按钮没有重置。要播放下一首歌曲,我必须双击,暂停播放并播放暂停(然后开始播放歌曲),自动播放下一首歌曲功能也不起作用,我猜是相关的?

第二个小问题是曲目名称没有改变。您可以看到的曲目的 HTML 行是

            <div class="song-title">Track1</div>

Visual of player

const background = document.querySelector('#background');
const thumbnail = document.querySelector('#thumbnail');
const song = document.querySelector('#song');

const songArtist = document.querySelector('.song-artist');
const songTitle = document.querySelector('.song-title');
const progressBar = document.querySelector('#progress-bar');
let pPause = document.querySelector('#play-pause');

songIndex = 0;
    songs = ['/music/track1.mp3',   '/music/track2.mp3',     '/music/track3.mp3',   '/music/track4.mp3',   '/music/track5.mp3',   '/music/track6.mp3',   '/music/track7.mp3'];
    thumbnails = ['/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', ];
    songArtists = ['Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT',];
    songTitles = ["Track1", "Track2", "Track3", "Track4", "Track5", "Track6", "Track7"];

let playing = true;
    function playPause()    {
        if (playing)    {
            const song = document.querySelector('#song'),
            thumbnail = document.querySelector('#thumbnail');

            pPause.src = "/images/pause-icon.png"
            thumbnail.style.transform = "scale(1.15)";

            song.play();
            playing = false;
        } else {
            pPause.src = "/images/play-icon.png"
            thumbnail.style.transform = "scale(1)";

            song.pause();
            playing = true;
        }
}

song.addEventListener('ended', function(){
    nextSong();
});

function nextSong() {
    songIndex++;
    if (songIndex === songs.length)  {
        songIndex = 0;
    };
    song.src = songs[songIndex];
    thumbnail.src = thumbnails[songIndex];
    background.src = thumbnails[songIndex];

    songArtist.innerHTML = songArtists[songIndex];
    songTitle.innerHTML = songTitles[songIndex];

    playing = true;
    playPause();
}

function previousSong() {
    songIndex--;
    if (songIndex < 0)  {
        songIndex = songs.length - 1;
    };
    song.src = songs[songIndex];
    thumbnail.src = thumbnails[songIndex];
    background.src = thumbnails[songIndex];

    songArtist.innerHTML = songArtists[songIndex];
    songTitle.innerHTML = songTitles[songIndex];

    playing = true;
    playPause();
}

function updateProgressValue()  {
    progressBar.max = song.duration;
    progressBar.value = song.currentTime;
    document.querySelector('.currentTime').innerHTML = (formatTime(Math.floor(song.currentTime)));
    if (document.querySelector('.durationTime').innerHTML === "NaN:NaN")    {
        document.querySelector('.durationTime').innerHTML = "0:00";
    }   else {
        document.querySelector('.durationTime').innerHTML = (formatTime(Math.floor(song.duration)));
    }
};

function formatTime(seconds)    {
    let min = Math.floor((seconds / 60));
    let sec = Math.floor(seconds - (min * 60));
    if (sec < 10){
        sec = `0${sec}`;
    };
    return `${min}:${sec}`;
};

setInterval (updateProgressValue, 500);

function changeProgressBar()    {
    song.currentTime    =   progressBar.value;
};

如果您需要任何 HTML,请告诉我。请注意,图标切换正常。感谢您的帮助,任何解释都将不胜感激:)!

【问题讨论】:

  • 我还想对我的帖子提出任何建设性的批评,它的格式是否正确,我是否添加了正确的代码并以清晰的方式?我刚开始使用这个网站。

标签: javascript audio audio-player


【解决方案1】:

差异

媒体标签&lt;audio&gt;&lt;video&gt;标签的通用名称。

  • OP 缺少任何event listenerson-event properties,因此可以安全地假设有on-event attributes

    <button onclick='lame()' type='button'>Click Me I'm Lame</button>
    

    不要使用它们,它们很烂。而是使用:

    <button type='button'>Click Me</button>
    
    const btn = document.queryselector('button');
    
    btn.addEventListener('click', clickHandler);
    
     /* OR */
    
    btn.onclick = clickHandler;
    
  • 有一个名为isPlaying 的布尔值。两个问题:

    1. 最初不应该是true。常识表明,一旦页面加载,song 就不会播放(如果是,为了用户体验重新考虑)。

    2. 不要使用标志,而是使用返回与媒体标签状态有关的布尔值的媒体属性(即song):song.pausedsong.endedsong.playing

  • nextSong() 的第一个if 条件是错误的:

    if (songIndex === songs.length)  {...
    

    songs.length 是 7。somgIndex 范围是 0 - 6。所以应该是 songs.length -1

  • 没有包含&lt;progress&gt; 标记及其处理程序,看起来它被正确复制了……几乎。这看起来好像是后来添加的:

    setInterval (updateProgressValue, 500);
    
    function changeProgressBar()    {
      song.currentTime = progressBar.value;
    };
    

    setInterval() 不能很好地替代 "timeupdate" 事件。媒体标签在播放时每 250 毫秒触发一次"timeupedate"。使用属性song.currentTimesong.duration 监控播放过程中经过的时间。

    changeProgressBar() 看起来没用。已经有一个名为updateProgressValue() 的处理程序。 changeProgressBar() 唯一的一行是 updateProgressValue() 中一行的反向版本:

    song.currentTime = progressBar.value;
    progressBar.value = song.currentTime;
    

建议

术语表单控件&lt;input&gt;&lt;output&gt;&lt;textarea&gt;&lt;button&gt;&lt;select&gt;、@的通用术语987654367@ 和 &lt;object&gt; 标记。
术语 祖先标记 是指在高于 targets 的 DOM 树——开发人员打算灌输由事件触发的行为的祖先标签的后代标签的术语。

  • 如果您的布局有多个表单控件,请将所有内容包装在 &lt;form&gt; 标记中。这样做允许您使用HTMLFormElement interfaceHTMLFormControlsCollection API。语法简洁,简化了对表单控件的访问。

     <form id='main'>
       <fieldset name='set'>
         <input>
         <button type='button'>
           [type='button'] is needed when nested within a <form>...
         </button>
         <button>
           ...otherwise it is a [type='submit'] by default.
         </button>
       </fieldset>
       <fieldset name='set'>
         <input id='input1'>
         <input id='input2'>
       </fieldset>
     </form>
     <script>
       /*
       - Referencing the <form> by #ID
       - Or bracket notation document.forms['main'];
       - Or by index document.forms[0];
       */
       const main = document.forms.main;
    
       /*
       - Once the <form> is referenced -- use the `.elements` property to collect 
         all of its form controls into a **HTMLCollection** (Use an short and easy
         to remember identifier to reference it).
       */
    
       const ui = main.elements; 
       /*
       - <form> tags and form controls can be referenced by [name] attribute as 
         well. Keep in mind that if there is more than one tag with the same     
         [name] -- then they will be collected into a **HTMLCollection**.
       - Note: There is another fieldset[name=set] -- so in this situation you can
         access them both: `sets[0]` and `sets[1]`
       */
       const sets = ui.set;
    
       /*
       - To reference a form control without a #ID or [name], its index is always
         available. There's an archaic way as well: `const input = ui.item(1)`
       - There's no need to reference the second <button> because its a 
         [type='submit'] tag. They have default behavior built-in: *Sends data to
         server* and *Resets the <form>*
       */
       const input = ui[1];
       const button = ui[2];
    
       /*
       - Of the many ways to reference a form control -- #ID is the easiest.
       */
       const i1 = ui.input1;
       const i2 = ui.input2;
    </script>
    

  • Event Delegation 是一种利用Event Bubbling 的编程模式,因此我们...

    • ...不仅可以监听未知且无限数量的标签触发的事件——我们还可以监听页面加载后动态创建的标签的触发事件。

    • 此外,只需要一个所有目标标签共有的祖先标签来侦听事件,(windowdocumentbody 始终可用,但仅在没有其他标签时使用更接近所有目标)。

      document.forms[0].onclick = controlPlayer; 
      
    • Event handler function 必须传递Event Object,以便.target 属性可用于确定与用户交互的实际标签(在本例中是点击了什么)。

      function controlPlayer(event) {
        const clicked = event.target;
      ...
      
    • 一旦确定,我们会进一步缩小可能性。这样做允许我们通过明确排除它们来排除我们不需要处理的标签:

      if (event.target.matches('[type=button]')) {...
      
  • 更改标记的外观以指示状态可以通过切换.classes 来完成,该.classes 具有它所代表的状态所特有的样式。通常的做法是使用pseudo-elements ::before and ::after


演示

const thumb = document.querySelector('.thumb');
const song = document.querySelector('#song');

const form = document.forms.ui;
const ui = form.elements;
const artist = ui.artist;
const title = ui.title;
const play = ui.playBack;
const prev = ui.prev;
const next = ui.next;

const base = 'https://glpjt.s3.amazonaws.com/so/av';
const songs = ['/mp3/righteous.mp3', '/mp3/jerky.mp3', '/mp3/nosegoblin.mp3', '/mp3/balls.mp3', '/mp3/behave.mp3'];
const thumbs = ['/img/link.gif', '/img/sol.png', '/img/ren.gif', '/img/balls.gif', '/img/austin.gif'];
const artists = ['Samuel L. Jackson', 'Sol Rosenberg', 'Ren', 'Al Pachino', 'Mike Myers'];
const titles = ["Righteous", "Whatnot", "Magic Nose Goblins", "Balls", "Behave"];
let index = 0;
let quantity = songs.length;

const playMP3 = () => {
  play.classList.remove('play');
  play.classList.add('pause');
  song.play();
}

const pauseMP3 = () => {
  play.classList.remove('pause');
  play.classList.add('play');
  song.pause();
}

form.onclick = controlPlayer;

song.onended = function(event) {
  pauseMP3();
  index++;
  if (index > quantity - 1) {
    index = 0;
  }
  song.src = base + songs[index];
  thumb.src = base + thumbs[index];
  artist.value = artists[index];
  title.value = titles[index];
  playMP3();
}

function init() {
  song.src = base + songs[0];
  thumb.src = base + thumbs[0];
  artist.value = artists[0];
  title.value = titles[0];
  song.load();
}

function controlPlayer(event) {
  const clicked = event.target;

  if (clicked.matches('#playBack')) {
    if (song.paused || song.ended) {
      playMP3();
    } else {
      pauseMP3();
    }
  }

  if (clicked.matches('#prev')) {
    pauseMP3();
    index--;
    if (index < 0) {
      index = quantity - 1;
    }
    song.src = base + songs[index];
    thumb.src = base + thumbs[index];
    artist.value = artists[index];
    title.value = titles[index];
    playMP3();
  }

  if (clicked.matches('#next')) {
    pauseMP3();
    index++;
    if (index > quantity - 1) {
      index = 0;
    }
    song.src = base + songs[index];
    thumb.src = base + thumbs[index];
    artist.value = artists[index];
    title.value = titles[index];
    playMP3();
  }
}

init();
:root,
body {
  font: 700 small-caps 2.5vw/1.5 Verdana;
}

b {
  display: inline-block;
  width: 6ch;
  color: white;
}

output {
  color: gold;
  text-shadow: 0 0 5px red;
}

button {
  display: inline-block;
  width: 3vw;
  height: 11.25vw;
  line-height: 11.25vw;
  border: 0;
  font: inherit;
  font-size: 3rem;
  vertical-align: middle;
  color: lime;
  background: none;
  cursor: pointer;
}

button:hover {
  color: cyan;
}

#playBack.play::before {
  content: '\0025b6';
}

#playBack.pause {
  height: 5.625vw;
  line-height: 5.625vw;
}

#playBack.pause::before {
  content: '\00258e\a0\00258e';
  height: 5.625vw;
  line-height: 5.625vw;
  font-size: 2rem;
  vertical-align: top;
}

#prev::before {
  content: '\0023ee';
  height: 5vw;
  line-height: 5vw;
  font-size: 3.25rem;
  vertical-align: top;
}

#next,
#prev {
  height: 5vw;
  line-height: 5vw;
}

#next::before {
  content: '\0023ed';
  height: 5vw;
  line-height: 5vw;
  font-size: 3.25rem;
  vertical-align: top;
}

figure {
  position: relative;
  min-height: 150px;
  margin: 0px auto;
}

figcaption {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1;
  height: max-content;
  padding: 3px 5px;
  font-size: 1.2rem;
}

figcaption label {
  background: rgba(0, 0, 0, 0.4);
}

.thumb {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 5px auto;
  object-fit: contain;
}

.controls {
  display: flex;
  flex-flow: row nowrap;
  justify-content: space-evenly;
  align-items: center;
  height: 6vw;
}
<main>
  <form id='ui'>
    <audio id='song'></audio>
    <fieldset>
      <figure>
        <img class='thumb'>
        <figcaption>
          <label>
          <b>Artist:</b> <output id='artist'></output>
        </label>
          <label><br>
          <b>Title:</b> <output id='title'></output>
        </label>
        </figcaption>
      </figure>
      <fieldset class='controls'>
        <button id='prev' type='button'></button>
        <button id='playBack' class='play' type='button'></button>
        <button id='next' type='button'></button>
      </fieldset>
    </fieldset>
  </form>
</main>

【讨论】:

  • nextSong() 的第一个 if 条件是错误的: if (songIndex === song.length) {... 这是从上一篇关于同一主题的帖子中获得的建议。很短,随便看看。它确实解决了我的问题。 stackoverflow.com/questions/61594245/…
  • @DecisiveDevelopment 你说的,"...自动播放 nextSong 功能也不起作用,..."。驱动nextSong() 的是index++ 尝试++index 因为if (songIndex === songs.length) {... 不是标准逻辑......??‍♂️。此外,我的代码完美无缺,我的条件非常标准。
  • 再次感谢您的帮助。我已经明确地查看了代码,并决定重新编写它。谢谢:)
  • 这是 gr8,编码愉快! ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 2014-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-17
相关资源
最近更新 更多