An HTML5 audio player with a play list and using Font Awesome to create a custom player interface. Custom seek/progress bar included that allows you to seek positions. Using Archive.org as my audio source.
A Pen by Craig Stroman on CodePen.
An HTML5 audio player with a play list and using Font Awesome to create a custom player interface. Custom seek/progress bar included that allows you to seek positions. Using Archive.org as my audio source.
A Pen by Craig Stroman on CodePen.
<div class="container"> | |
<audio id="audio" preload="none" tabindex="0"> | |
<source src="https://archive.org/download/calexico2006-12-02..flac16/calexico2006-12-02d1t02.mp3" data-track-number="1" /> | |
<source src="https://archive.org/download/ra2007-07-21/ra2007-07-21d1t05_64kb.mp3" data-track-number="2" /> | |
<source src="https://archive.org/download/slac2002-02-15/slac2002-02-15d1t07_64kb.mp3" data-track-number="3" /> | |
<source src="https://archive.org/download/blitzentrapper2009-02-24.flac16/blitzentrapper2009-02-24t02_64kb.mp3" data-track-number="4" /> | |
<source src="https://archive.org/download/samples2003-11-21.flac16/samples2003-11-21d2t04.mp3" data-track-number="5" /> | |
<source src="https://archive.org/download/mikedoughty2004-06-16.flac16/d1t13.mp3" data-track-number="6" /> | |
<source src="https://archive.org/download/glove2004-03-18.shnf/glove2004-03-18d1t05.mp3" data-track-number="7" /> | |
<source src="https://archive.org/download/guster2005-11-12.flac16/guster2005-11-12d2t04.mp3" data-track-number="8" /> | |
<source src="https://archive.org/download/oar2004-11-12.flac/oar2004-11-12d1t01.mp3" data-track-number="9" /> | |
<source src="https://archive.org/download/mmj2003-09-26.shnf/mmj2003-09-26d2t08.mp3" data-track-number="10" /> | |
Your browser does not support HTML5 audio. | |
</audio> | |
<div class="player"> | |
<div class="large-toggle-btn"> | |
<i class="large-play-btn"><span class="screen-reader-text">Large toggle button</span></i> | |
</div> | |
<!-- /.play-box --> | |
<div class="info-box"> | |
<div class="track-info-box"> | |
<div class="track-title-text"></div> | |
<div class="audio-time"> | |
<span class="current-time">00:00</span> / | |
<span class="duration">00:00</span> | |
</div> | |
</div> | |
<!-- /.info-box --> | |
<div class="progress-box"> | |
<div class="progress-cell"> | |
<div class="progress"> | |
<div class="progress-buffer"></div> | |
<div class="progress-indicator"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- /.progress-box --> | |
<div class="controls-box"> | |
<i class="previous-track-btn disabled"><span class="screen-reader-text">Previous track button</span></i> | |
<i class="next-track-btn"><span class="screen-reader-text">Next track button</span></i> | |
</div> | |
<!-- /.controls-box --> | |
</div> | |
<!-- /.player --> | |
<div class="play-list"> | |
<div class="play-list-row" data-track-row="1"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
1. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="1">Calexico - Across The Wire</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="2"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
2. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="2">Ryan Adams & The Cardinals - Cold Roses</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="3"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
3. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="3">The Slackers - Married Girl</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="4"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
4. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="4">Blitzen Trapper - Saturday Night</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="5"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
5. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="5">The Samples - Feel Us Shaking</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="6"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
6. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="6">Mike Doughty - American Car</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="7"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
7. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="7">G. Love & Special Sauce - Dreamin'</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="8"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
8. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="8">Guster - Amsterdam</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="9"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
9. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="9">O.A.R. - About Mr. Brown</a> | |
</div> | |
</div> | |
<div class="play-list-row" data-track-row="10"> | |
<div class="small-toggle-btn"> | |
<i class="small-play-btn"><span class="screen-reader-text">Small toggle button</span></i> | |
</div> | |
<div class="track-number"> | |
10. | |
</div> | |
<div class="track-title"> | |
<a class="playlist-track" href="#" data-play-track="10">My Morning Jacket - Phone Went West</a> | |
</div> | |
</div> | |
</div> | |
<div>Music from the <a href="https://archive.org/details/etree" target="_blank">Live Music Archive</a></div> | |
</div> |
var audioPlayer = function() { | |
"use strict"; | |
// Private variables | |
var _currentTrack = null; | |
var _elements = { | |
audio: document.getElementById("audio"), | |
playerButtons: { | |
largeToggleBtn: document.querySelector(".large-toggle-btn"), | |
nextTrackBtn: document.querySelector(".next-track-btn"), | |
previousTrackBtn: document.querySelector(".previous-track-btn"), | |
smallToggleBtn: document.getElementsByClassName("small-toggle-btn") | |
}, | |
progressBar: document.querySelector(".progress-box"), | |
playListRows: document.getElementsByClassName("play-list-row"), | |
trackInfoBox: document.querySelector(".track-info-box") | |
}; | |
var _playAHead = false; | |
var _progressCounter = 0; | |
var _progressBarIndicator = _elements.progressBar.children[0].children[0].children[1]; | |
var _trackLoaded = false; | |
/** | |
* Determines the buffer progress. | |
* | |
* @param audio The audio element on the page. | |
**/ | |
var _bufferProgress = function(audio) { | |
var bufferedTime = (audio.buffered.end(0) * 100) / audio.duration; | |
var progressBuffer = _elements.progressBar.children[0].children[0].children[0]; | |
progressBuffer.style.width = bufferedTime + "%"; | |
}; | |
/** | |
* A utility function for getting the event cordinates based on browser type. | |
* | |
* @param e The JavaScript event. | |
**/ | |
var _getXY = function(e) { | |
var containerX = _elements.progressBar.offsetLeft; | |
var containerY = _elements.progressBar.offsetTop; | |
var coords = { | |
x: null, | |
y: null | |
}; | |
var isTouchSuopported = "ontouchstart" in window; | |
if (isTouchSuopported) { //For touch devices | |
coords.x = e.clientX - containerX; | |
coords.y = e.clientY - containerY; | |
return coords; | |
} else if (e.offsetX || e.offsetX === 0) { // For webkit browsers | |
coords.x = e.offsetX; | |
coords.y = e.offsetY; | |
return coords; | |
} else if (e.layerX || e.layerX === 0) { // For Mozilla firefox | |
coords.x = e.layerX; | |
coords.y = e.layerY; | |
return coords; | |
} | |
}; | |
var _handleProgressIndicatorClick = function(e) { | |
var progressBar = document.querySelector(".progress-box"); | |
var xCoords = _getXY(e).x; | |
return (xCoords - progressBar.offsetLeft) / progressBar.children[0].offsetWidth; | |
}; | |
/** | |
* Initializes the html5 audio player and the playlist. | |
* | |
**/ | |
var initPlayer = function() { | |
if (_currentTrack === 1 || _currentTrack === null) { | |
_elements.playerButtons.previousTrackBtn.disabled = true; | |
} | |
//Adding event listeners to playlist clickable elements. | |
for (var i = 0; i < _elements.playListRows.length; i++) { | |
var smallToggleBtn = _elements.playerButtons.smallToggleBtn[i]; | |
var playListLink = _elements.playListRows[i].children[2].children[0]; | |
//Playlist link clicked. | |
playListLink.addEventListener("click", function(e) { | |
e.preventDefault(); | |
var selectedTrack = parseInt(this.parentNode.parentNode.getAttribute("data-track-row")); | |
if (selectedTrack !== _currentTrack) { | |
_resetPlayStatus(); | |
_currentTrack = null; | |
_trackLoaded = false; | |
} | |
if (_trackLoaded === false) { | |
_currentTrack = parseInt(selectedTrack); | |
_setTrack(); | |
} else { | |
_playBack(this); | |
} | |
}, false); | |
//Small toggle button clicked. | |
smallToggleBtn.addEventListener("click", function(e) { | |
e.preventDefault(); | |
var selectedTrack = parseInt(this.parentNode.getAttribute("data-track-row")); | |
if (selectedTrack !== _currentTrack) { | |
_resetPlayStatus(); | |
_currentTrack = null; | |
_trackLoaded = false; | |
} | |
if (_trackLoaded === false) { | |
_currentTrack = parseInt(selectedTrack); | |
_setTrack(); | |
} else { | |
_playBack(this); | |
} | |
}, false); | |
} | |
//Audio time has changed so update it. | |
_elements.audio.addEventListener("timeupdate", _trackTimeChanged, false); | |
//Audio track has ended playing. | |
_elements.audio.addEventListener("ended", function(e) { | |
_trackHasEnded(); | |
}, false); | |
//Audio error. | |
_elements.audio.addEventListener("error", function(e) { | |
switch (e.target.error.code) { | |
case e.target.error.MEDIA_ERR_ABORTED: | |
alert('You aborted the video playback.'); | |
break; | |
case e.target.error.MEDIA_ERR_NETWORK: | |
alert('A network error caused the audio download to fail.'); | |
break; | |
case e.target.error.MEDIA_ERR_DECODE: | |
alert('The audio playback was aborted due to a corruption problem or because the video used features your browser did not support.'); | |
break; | |
case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED: | |
alert('The video audio not be loaded, either because the server or network failed or because the format is not supported.'); | |
break; | |
default: | |
alert('An unknown error occurred.'); | |
break; | |
} | |
trackLoaded = false; | |
_resetPlayStatus(); | |
}, false); | |
//Large toggle button clicked. | |
_elements.playerButtons.largeToggleBtn.addEventListener("click", function(e) { | |
if (_trackLoaded === false) { | |
_currentTrack = parseInt(1); | |
_setTrack() | |
} else { | |
_playBack(); | |
} | |
}, false); | |
//Next track button clicked. | |
_elements.playerButtons.nextTrackBtn.addEventListener("click", function(e) { | |
if (this.disabled !== true) { | |
_currentTrack++; | |
_trackLoaded = false; | |
_resetPlayStatus(); | |
_setTrack(); | |
} | |
}, false); | |
//Previous track button clicked. | |
_elements.playerButtons.previousTrackBtn.addEventListener("click", function(e) { | |
if (this.disabled !== true) { | |
_currentTrack--; | |
_trackLoaded = false; | |
_resetPlayStatus(); | |
_setTrack(); | |
} | |
}, false); | |
//User is moving progress indicator. | |
_progressBarIndicator.addEventListener("mousedown", _mouseDown, false); | |
//User stops moving progress indicator. | |
window.addEventListener("mouseup", _mouseUp, false); | |
}; | |
/** | |
* Handles the mousedown event by a user and determines if the mouse is being moved. | |
* | |
* @param e The event object. | |
**/ | |
var _mouseDown = function(e) { | |
window.addEventListener("mousemove", _moveProgressIndicator, true); | |
audio.removeEventListener("timeupdate", _trackTimeChanged, false); | |
_playAHead = true; | |
}; | |
/** | |
* Handles the mouseup event by a user. | |
* | |
* @param e The event object. | |
**/ | |
var _mouseUp = function(e) { | |
if (_playAHead === true) { | |
var duration = parseFloat(audio.duration); | |
var progressIndicatorClick = parseFloat(_handleProgressIndicatorClick(e)); | |
window.removeEventListener("mousemove", _moveProgressIndicator, true); | |
audio.currentTime = duration * progressIndicatorClick; | |
audio.addEventListener("timeupdate", _trackTimeChanged, false); | |
_playAHead = false; | |
} | |
}; | |
/** | |
* Moves the progress indicator to a new point in the audio. | |
* | |
* @param e The event object. | |
**/ | |
var _moveProgressIndicator = function(e) { | |
var newPosition = 0; | |
var progressBarOffsetLeft = _elements.progressBar.offsetLeft; | |
var progressBarWidth = 0; | |
var progressBarIndicator = _elements.progressBar.children[0].children[0].children[1]; | |
var progressBarIndicatorWidth = _progressBarIndicator.offsetWidth; | |
var xCoords = _getXY(e).x; | |
progressBarWidth = _elements.progressBar.children[0].offsetWidth - progressBarIndicatorWidth; | |
newPosition = xCoords - progressBarOffsetLeft; | |
if ((newPosition >= 1) && (newPosition <= progressBarWidth)) { | |
progressBarIndicator.style.left = newPosition + ".px"; | |
} | |
if (newPosition < 0) { | |
progressBarIndicator.style.left = "0"; | |
} | |
if (newPosition > progressBarWidth) { | |
progressBarIndicator.style.left = progressBarWidth + "px"; | |
} | |
}; | |
/** | |
* Controls playback of the audio element. | |
* | |
**/ | |
var _playBack = function() { | |
if (_elements.audio.paused) { | |
_elements.audio.play(); | |
_updatePlayStatus(true); | |
document.title = "\u25B6 " + document.title; | |
} else { | |
_elements.audio.pause(); | |
_updatePlayStatus(false); | |
document.title = document.title.substr(2); | |
} | |
}; | |
/** | |
* Sets the track if it hasn't already been loaded yet. | |
* | |
**/ | |
var _setTrack = function() { | |
var songURL = _elements.audio.children[_currentTrack - 1].src; | |
_elements.audio.setAttribute("src", songURL); | |
_elements.audio.load(); | |
_trackLoaded = true; | |
_setTrackTitle(_currentTrack, _elements.playListRows); | |
_setActiveItem(_currentTrack, _elements.playListRows); | |
_elements.trackInfoBox.style.visibility = "visible"; | |
_playBack(); | |
}; | |
/** | |
* Sets the activly playing item within the playlist. | |
* | |
* @param currentTrack The current track number being played. | |
* @param playListRows The playlist object. | |
**/ | |
var _setActiveItem = function(currentTrack, playListRows) { | |
for (var i = 0; i < playListRows.length; i++) { | |
playListRows[i].children[2].className = "track-title"; | |
} | |
playListRows[currentTrack - 1].children[2].className = "track-title active-track"; | |
}; | |
/** | |
* Sets the text for the currently playing song. | |
* | |
* @param currentTrack The current track number being played. | |
* @param playListRows The playlist object. | |
**/ | |
var _setTrackTitle = function(currentTrack, playListRows) { | |
var trackTitleBox = document.querySelector(".player .info-box .track-info-box .track-title-text"); | |
var trackTitle = playListRows[currentTrack - 1].children[2].outerText; | |
trackTitleBox.innerHTML = null; | |
trackTitleBox.innerHTML = trackTitle; | |
document.title = trackTitle; | |
}; | |
/** | |
* Plays the next track when a track has ended playing. | |
* | |
**/ | |
var _trackHasEnded = function() { | |
parseInt(_currentTrack); | |
_currentTrack = (_currentTrack === _elements.playListRows.length) ? 1 : _currentTrack + 1; | |
_trackLoaded = false; | |
_resetPlayStatus(); | |
_setTrack(); | |
}; | |
/** | |
* Updates the time for the song being played. | |
* | |
**/ | |
var _trackTimeChanged = function() { | |
var currentTimeBox = document.querySelector(".player .info-box .track-info-box .audio-time .current-time"); | |
var currentTime = audio.currentTime; | |
var duration = audio.duration; | |
var durationBox = document.querySelector(".player .info-box .track-info-box .audio-time .duration"); | |
var trackCurrentTime = _trackTime(currentTime); | |
var trackDuration = _trackTime(duration); | |
currentTimeBox.innerHTML = null; | |
currentTimeBox.innerHTML = trackCurrentTime; | |
durationBox.innerHTML = null; | |
durationBox.innerHTML = trackDuration; | |
_updateProgressIndicator(audio); | |
_bufferProgress(audio); | |
}; | |
/** | |
* A utility function for converting a time in miliseconds to a readable time of minutes and seconds. | |
* | |
* @param seconds The time in seconds. | |
* | |
* @return time The time in minutes and/or seconds. | |
**/ | |
var _trackTime = function(seconds) { | |
var min = 0; | |
var sec = Math.floor(seconds); | |
var time = 0; | |
min = Math.floor(sec / 60); | |
min = min >= 10 ? min : '0' + min; | |
sec = Math.floor(sec % 60); | |
sec = sec >= 10 ? sec : '0' + sec; | |
time = min + ':' + sec; | |
return time; | |
}; | |
/** | |
* Updates both the large and small toggle buttons accordingly. | |
* | |
* @param audioPlaying A booean value indicating if audio is playing or paused. | |
**/ | |
var _updatePlayStatus = function(audioPlaying) { | |
if (audioPlaying) { | |
_elements.playerButtons.largeToggleBtn.children[0].className = "large-pause-btn"; | |
_elements.playerButtons.smallToggleBtn[_currentTrack - 1].children[0].className = "small-pause-btn"; | |
} else { | |
_elements.playerButtons.largeToggleBtn.children[0].className = "large-play-btn"; | |
_elements.playerButtons.smallToggleBtn[_currentTrack - 1].children[0].className = "small-play-btn"; | |
} | |
//Update next and previous buttons accordingly | |
if (_currentTrack === 1) { | |
_elements.playerButtons.previousTrackBtn.disabled = true; | |
_elements.playerButtons.previousTrackBtn.className = "previous-track-btn disabled"; | |
} else if (_currentTrack > 1 && _currentTrack !== _elements.playListRows.length) { | |
_elements.playerButtons.previousTrackBtn.disabled = false; | |
_elements.playerButtons.previousTrackBtn.className = "previous-track-btn"; | |
_elements.playerButtons.nextTrackBtn.disabled = false; | |
_elements.playerButtons.nextTrackBtn.className = "next-track-btn"; | |
} else if (_currentTrack === _elements.playListRows.length) { | |
_elements.playerButtons.nextTrackBtn.disabled = true; | |
_elements.playerButtons.nextTrackBtn.className = "next-track-btn disabled"; | |
} | |
}; | |
/** | |
* Updates the location of the progress indicator according to how much time left in audio. | |
* | |
**/ | |
var _updateProgressIndicator = function() { | |
var currentTime = parseFloat(_elements.audio.currentTime); | |
var duration = parseFloat(_elements.audio.duration); | |
var indicatorLocation = 0; | |
var progressBarWidth = parseFloat(_elements.progressBar.offsetWidth); | |
var progressIndicatorWidth = parseFloat(_progressBarIndicator.offsetWidth); | |
var progressBarIndicatorWidth = progressBarWidth - progressIndicatorWidth; | |
indicatorLocation = progressBarIndicatorWidth * (currentTime / duration); | |
_progressBarIndicator.style.left = indicatorLocation + "px"; | |
}; | |
/** | |
* Resets all toggle buttons to be play buttons. | |
* | |
**/ | |
var _resetPlayStatus = function() { | |
var smallToggleBtn = _elements.playerButtons.smallToggleBtn; | |
_elements.playerButtons.largeToggleBtn.children[0].className = "large-play-btn"; | |
for (var i = 0; i < smallToggleBtn.length; i++) { | |
if (smallToggleBtn[i].children[0].className === "small-pause-btn") { | |
smallToggleBtn[i].children[0].className = "small-play-btn"; | |
} | |
} | |
}; | |
return { | |
initPlayer: initPlayer | |
}; | |
}; | |
(function() { | |
var player = new audioPlayer(); | |
player.initPlayer(); | |
})(); |
@import "//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.css"; | |
//General mixins | |
//Clearfix mixin | |
@mixin clearfix() { | |
*zoom: 1; | |
&:before, &:after { | |
content: " "; | |
display: table; | |
} | |
&:after { | |
clear: both; | |
display: block; | |
font-size: 0; | |
height: 0; | |
visibility: hidden; | |
} | |
} | |
//Media query mixin | |
@mixin media($type) { | |
@if $type == tablet {/* Small devices (tablets, 768px and up) */ | |
@media (min-width: 768px) and (max-width: 991px) { | |
@content; | |
} | |
} | |
@else if $type == mediumDesktop {/* Medium devices (desktops, 992px and up) */ | |
@media (min-width: 992px) and (max-width: 1100px) { | |
@content; | |
} | |
} | |
@else if $type == largeDesktop {/* Large devices (large desktops, 1200px and up) */ | |
@media (min-width: 1200px) { | |
@content; | |
} | |
} | |
} | |
//General classes | |
.screen-reader-text { /* Reusable, toolbox kind of class */ | |
position: absolute; | |
top: -9999px; | |
left: -9999px; | |
} | |
.disabled { | |
color: #666; | |
cursor: default; | |
} | |
.show { | |
display: inline-block !important; | |
} | |
//General CSS | |
body { | |
margin: 10px 0 0 0; | |
.container { | |
font-family: arial, helvetica, sans-serif; | |
font-size: 1em; | |
margin: 0 auto; | |
width: 500px; | |
.player { | |
height: 60px; | |
margin: 0; | |
position: relative; | |
width: 400px; | |
@include media(tablet) { | |
width: 470px; | |
} | |
@include media(mediumDesktop) { | |
width: 470px; | |
} | |
@include media(largeDesktop) { | |
width: 470px; | |
} | |
.large-toggle-btn { | |
border: 1px solid #d9d9d9; | |
border-radius: 2px; | |
float: left; | |
font-size: 1.5em; | |
height: 50px; | |
margin: 0 10px 0 0; | |
overflow: hidden; | |
padding: 5px 0 0 0; | |
position: relative; | |
text-align: center; | |
vertical-align: bottom; | |
width: 54px; | |
.large-play-btn { | |
&:before { | |
content: "\f04b"; | |
font: 1.5em/1.75 "FontAwesome"; | |
} | |
cursor: pointer; | |
display: inline-block; | |
position: relative; | |
top: -14%; | |
} | |
.large-pause-btn { | |
&:before { | |
content: "\f04c"; | |
font: 1.5em/1.75 "FontAwesome"; | |
} | |
cursor: pointer; | |
display: inline-block; | |
position: relative; | |
top: -13%; | |
} | |
}//end .play-box | |
.info-box { | |
bottom: 10px; | |
left: 65px; | |
position: absolute; | |
top: 15px; | |
.track-info-box { | |
float: left; | |
font-size: 12px; | |
margin: 0 0 6px 0; | |
visibility: hidden; | |
width: 400px; | |
.track-title-text { | |
display: inline-block; | |
} | |
.audio-time { | |
display: inline-block; | |
padding: 0 0 0 5px; | |
width: 80px; | |
} | |
@include clearfix(); | |
} | |
} | |
.progress-box { | |
float: left; | |
min-width: 270px; | |
position: relative; | |
.progress-cell { | |
height: 12px; | |
position: relative; | |
.progress { | |
background: #fff; | |
border: 1px solid #d9d9d9; | |
height: 8px; | |
position: relative; | |
width: auto; | |
.progress-buffer { | |
background: #337ab7; | |
height: 100%; | |
width: 0; | |
} | |
.progress-indicator { | |
background: #fff; | |
border: 1px solid #bebebe; | |
border-radius: 3px; | |
cursor: pointer; | |
height: 10px; | |
left: 0; | |
overflow: hidden; | |
position: absolute; | |
top: -2px; | |
width: 22px; | |
} | |
} | |
} | |
}//end .prgoress-box | |
.controls-box { | |
bottom: 10px; | |
left: 350px; | |
position: absolute; | |
.previous-track-btn { | |
&:before { | |
content: "\f049"; | |
font: 1em "FontAwesome"; | |
} | |
cursor: pointer; | |
display: inline-block; | |
} | |
.next-track-btn { | |
&:before { | |
content: "\f050"; | |
font: 1em "FontAwesome"; | |
} | |
cursor: pointer; | |
display: inline-block; | |
} | |
} | |
@include clearfix(); | |
}//end .player | |
.play-list { | |
display: block; | |
margin: 0 auto 20px auto; | |
width: 100%; | |
.play-list-row { | |
display: block; | |
margin: 10px 0; | |
width: 100%; | |
.track-title { | |
.playlist-track { | |
&:hover { | |
text-decoration: underline; | |
} | |
color: #000; | |
text-decoration: none; | |
} | |
} | |
.small-toggle-btn { | |
border: 1px solid #d9d9d9; | |
border-radius: 2px; | |
cursor: pointer; | |
display: inline-block; | |
height: 20px; | |
margin: 0 auto; | |
overflow: hidden; | |
position: relative; | |
text-align: center; | |
vertical-align: middle; | |
width: 20px; | |
.small-play-btn { | |
&:before { | |
content: "\f04b"; | |
font: 0.85em "FontAwesome"; | |
} | |
display: inline-block; | |
} | |
.small-pause-btn { | |
&:before { | |
content: "\f04c"; | |
font: 0.85em "FontAwesome"; | |
} | |
display: inline-block; | |
} | |
} | |
.track-number { | |
display: inline-block; | |
} | |
.track-title { | |
display: inline-block; | |
.playlist-track { | |
text-decoration: none; | |
&:hover { | |
text-decoration: underline; | |
} | |
} | |
} | |
.track-title.active-track { | |
font-weight: bold; | |
} | |
@include clearfix(); | |
} | |
} | |
}//end .container | |
} |
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" /> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" /> |