Select Page

Build a Music Player With Vuetify

Final product image
What You’ll Be Creating

Building apps with Vue.js is easy, fun, and enjoyable. You can build a working app with minimum effort. To prove that, today I’ll show you how easy it is to build your own full-featured music player. To make things even easier, we’ll use Vuetify.js, a Vue.js powered UI library, which will speed up the UI construction. I can almost feel your impatience, so let’s get started. 

You can find the full source code in the GitHub repo. And here is the working demo. To follow this tutorial, you should be familiar with Vue componentsVue single file components, and ES2015 syntax.

Planning the App

Every creation starts with an idea and at least some basic planning. So first we need to decide what we want to build and what functionality we want to implement. It’s said that a picture is worth a thousand words, so let’s start with a simple sketch of the music player.

Vue music player wireframe sketch

I made this wireframe so you can get a general notion of the UI we want to build. The next step is to describe the functionality we intend to implement.

As John Johnson says: 

First, solve the problem. Then, write the code.

We’ll use that as a source of wisdom, and we’ll plan the app out before we start coding it.

App Components

Vue.js is a component-based framework. So we need first to split the app into individual components (five in our case, as shown in the sketch above), and to outline the features and functionality for each of them. 

Title Bar

This component will contain the following parts:

  • a menu on the left side
  • the name of the app in the center
  • three static icons on the right side

Info Panel

This component will show basic information about the currently played track:

  • the track’s artist and title on the left side
  • the current track’s position and duration on the right side

Control Bars

This component will contain two bars, which will include all the controls necessary to manipulate the audio tracks in the player’s playlist.

  • a volume slider with an icon on the left (its appearance will change according to the volume level and when the sound is muted), and volume percentage on the right
  • buttons for playing, pausing, stopping, and skipping the tracks.
  • two buttons on the far right: one for repeating the current track, and one for shuffling the tracks’ order of playing
  • a seek bar showing the currently played track’s position, with the ability to change it with a mouse click on the bar

Playlist Panel

This component will contain the tracks’ playlist with the following functionality:

  • display a track with the proper number, artist, title, and duration properties
  • select a track on single click
  • play a track on double click

Search Bar

This component will offer search functionality in the cases when we want to find and play particular track(s).

Of course, the above outline cannot cover all the details and nuances, and this is perfectly fine. For now, it’s enough for us to get an overall picture of the final product. We’ll handle all the details and eventual challenges during the building process.

So let’s get into the fun part and write some code!

Getting Started

Vuetify’s quick start page offers plenty of options to get you started. We’ll use one of the pre-made Vue CLI templates called Webpack Simple. Run the following commands in the directory you want to use for this project:

First, install Vue CLI:

Then, create the app: 

Next, go to the app’s directory and install all dependencies: 

We’ll use Howler.js (a JavaScript audio library) to handle the audio parts of the music player. So we need to include it in the project too. Run the following command:

And finally, run the app:

The app will open on localhost:8080 in your default browser. You should see a simple Vuetify app skeleton. 

Tweaking the Template

To adjust it to our needs, we need to clean up the template and tweak it a bit. Rename the App.vue file to Player.vue, open it, delete everything inside, and add the following instead:

We wrap our music player app in the v-app component, which is required for the app to work properly. We also pass the dark prop, to apply the Vuetify dark theme.

Now, open the main.js file, delete the original content, and add the following:

Also, open the index.html file and change the content of the </code> tag to <em>Vue Music Player</em>.</p> <p>Now, in your browser, you should see an empty dark page. And voila. You are ready to start creating.</p> <p>Before you start coding, it’s good to know that Vuetify offers code snippets and autocompletion for the major code editors: VS Code, Atom, and Sublime. To get the snippets, search for the extension in your favorite editor (<code class="inline">vuetify-vscode</code>, or <code class="inline">vuetify-atom</code>, or <code class="inline">vuetify-sublime</code>).</p> <h2>Build the Title Bar Component</h2> <p>In the <strong>src</strong> directory, create a new <strong>components</strong> folder. Then, in that folder, create the <strong>PlayerTitleBar.vue</strong> file with the following content: </p> <pre class="brush: html noskimlinks noskimwords"> headset MENU About <h2>Vue Music Player</h2> Version 1.0.0 OK VUE MUSIC PLAYER remove check_box_outline_blank close export default { data () { return { dialog: false } }, } </pre> <p>Here, we use the following Vuetify components: <a href="https://vuetifyjs.com/en/components/toolbars#example-system-bars-status-window" target="_self">toolbar</a>, <a href="https://vuetifyjs.com/en/components/menus" target="_self">menu</a>, <a href="https://vuetifyjs.com/en/components/buttons#example-outline" target="_self">button</a>, <a href="https://vuetifyjs.com/en/components/icons" target="_self">icon</a>, <a href="https://vuetifyjs.com/en/components/lists" target="_self">list</a>, <a href="https://vuetifyjs.com/en/components/dialogs" target="_self">dialog</a>, and <a href="https://vuetifyjs.com/en/components/cards" target="_self">card</a>. </p> <p>We separate the menu, the name, and the icons with the <code class="inline"></code> component. To show or hide the dialog, we create the <code class="inline">dialog: false</code> data property. Its value will toggle when we click on the <em>About</em> menu item.</p> <p>Now, in the <strong>Player.vue</strong> file, import the title bar component, register it in the components object, and add it in the template.</p> <pre class="brush: html noskimlinks noskimwords"> // ADD the component in the template import PlayerTitleBar from './components/PlayerTitleBar.vue' // IMPORT the component export default { components: { PlayerTitleBar // REGISTER the component }, data () { return { } } } </pre> <p>Now, check the result in your browser. You should see the following:</p> <figure class="post_image"><img alt="The player title bar"></figure> <p>We’ll repeat these three steps for the other four components. So when in the next sections I tell you to import, register and add a component in the template, you should follow the same procedure described here.</p> <h2>Build the Playlist Component<br /> </h2> <p>In the root directory, create a new <strong>playlist</strong> folder and add the audio files you want to play. The file names must be written with underscores between the words and a <strong>.mp3</strong> extension at the end—for example, <strong>Remember_the_Way.mp3</strong>. Now, create an audio tracks array inside <strong>Player.vue</strong>‘s data object: </p> <pre class="brush: javascript noskimlinks noskimwords">playlist: [ {title: "Streets of Sant'Ivo", artist: "Ask Again", howl: null, display: true}, {title: "Remember the Way", artist: "Ask Again", howl: null, display: true}, ... ]</pre> <p>Each track has <code class="inline">title</code> and <code class="inline">artist</code> properties, a <code class="inline">howl</code> object set to <code class="inline">null</code>, and a <code class="inline">display</code> property set to <code class="inline">true</code>. </p> <p>The <code class="inline">display</code> property will be used when we implement the search functionality. Now it is set to <code class="inline">true</code> for all tracks, so all of them are visible.</p> <p>Howler wraps an audio file in a <code class="inline">howl</code> object. We set <code class="inline">howl</code> to <code class="inline">null</code> because we’ll populate it dynamically at the creation of the Vue instance. To do that, we use the Vue’s <code class="inline">created</code> <a href="https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks" target="_self">lifecycle hook</a>. </p> <pre class="brush: javascript noskimlinks noskimwords">created: function () { this.playlist.forEach( (track) => { let file = track.title.replace(/s/g, "_") track.howl = new Howl({ src: [`./playlist/${file}.mp3`] }) }) }</pre> <p>This will set a new <code class="inline">Howl</code> object for each track in the playlist.</p> <p>Now, create the <strong>PlayerPlaylistPanel.vue</strong> component and add this inside: </p> <pre class="brush: html noskimlinks noskimwords"> {{ index }} {{ track.artist }} - {{ track.title }} {{ track.howl.duration() }} export default { props: { playlist: Array } } </pre> <p>First, we pass the prop <code class="inline">playlist</code> from the <strong>Player.vue</strong> file. Next, in the template, we go through each track with the <code class="inline">v-for</code> directive and display the track’s index, followed by the track’s artist and title, and the duration of the track on the far right. We also use <code class="inline">v-show</code> bound to the <code class="inline">display</code> property. A track will be visible only if <code class="inline">display</code> is <code class="inline">true</code>.</p> <p>Now, in the <strong>Player.vue</strong> file, we import, register, and add the playlist component in the template. Then, we bind the <code class="inline">playlist</code> prop to the <code class="inline">playlist</code> data property like this: <code class="inline"></code>.</p> <p>Let’s check the result in the browser:</p> <figure class="post_image"><img alt="The player playlist panel"></figure> <p>There are two problems here. First, the numbers of the tracks are not correct, and second, the track’s duration is shown in milliseconds, but we want it to be in minutes. We’ll fix each of these issues by creating a formatting filter. </p> <p>In the <strong>main.js</strong> file, create a <code class="inline">numbers</code> <a href="https://github.com/codeknack/vue-music-player/blob/master/src/main.js" target="_self">filter</a> and a <code class="inline">minutes</code> filter, which will be globally accessible. Next, in <strong>PlayerPlaylistPanel.vue</strong>, we use them like this: <code class="inline">{{ index | numbers }}</code> and <code class="inline">{{ track.howl.duration() | minutes }}</code>.</p> <p>Now, if you check the app, everything should display correctly. </p> <figure class="post_image"><img alt="The player playlist panel with fixed numbers and minutes"></figure> <h3>Make Tracks Selectable<br /> </h3> <p>In the <strong>Player.vue</strong> file, add the <code class="inline">selectedTrack: null</code> data property and bind it to the playlist component (<code class="inline">:selectedTrack="selectedTrack"</code>). Then, we pass the prop in the <strong>PlayerPlaylistPanel.vue</strong> file (<code class="inline">selectedTrack: Object</code>).  </p> <p>We also add a click event listener to <code class="inline"></code> and then create the <code class="inline">selectTrack()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">methods: { selectTrack (track) { this.$emit('selecttrack', track) } } </pre> <p>Now, back in <code class="inline">Player.vue</code>, add the <code class="inline">selecttrack</code> event to the playlist component (<code class="inline">@selecttrack="selectTrack"</code>) and create the <code class="inline">selectTrack()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">selectTrack (track) { this.selectedTrack = track }</pre> <p>Now, if you go to the playlist and click on a track, it will be selected. We can’t see it, but we can prove it in the Vue DevTools. In the following screenshot, the second track is selected:</p> <figure class="post_image"><img alt="The player playlist with selected track shown in DevTools "></figure> <h4>Row and Selection Styling</h4> <p>The next step is to make the selection visible. To do it, we’ll bind a class which will color the selected track in orange and another class which will make even rows darker to make the tracks more distinguishable. Put the following after the <code class="inline">v-show</code> directive:</p> <pre class="brush: html noskimlinks noskimwords">:class="[{selected: track === selectedTrack}, {even: index % 2 == 0}]" </pre> <p>We’ll also add another class, which will show a scrollbar when the list gets too big.</p> <pre class="brush: html noskimlinks noskimwords"></pre> <p>We add the necessary classes at the end of the file. </p> <pre class="brush: css noskimlinks noskimwords"> .selected { background-color: orange !important; } .even { background-color: #505050 } .playlist { overflow: auto } </pre> <p>And that’s it. Now, the selected track is highlighted in orange.</p> <figure class="post_image"><img alt="The player playlist with selected track colored"></figure> <p>We’ll add the double click play functionality at the end of the next section.</p> <h2>Build the Player Controls Component<br /> </h2> <p>Let’s create the player controls now. We’ll start with the play, pause, and stop buttons. </p> <h3>Add the Play, Pause, and Stop Buttons<br /> </h3> <p>Create the <strong>PlayerControlsBars.vue</strong> component and add this inside: </p> <pre class="brush: html noskimlinks noskimwords"> <div> stop play_arrow pause </div> </pre> <p>Here, we use the Vuetify <a href="https://vuetifyjs.com/en/components/toolbars" target="_self">toolbar</a> component.</p> <p>There are three buttons with registered click event listeners. Let’s create the methods for them: </p> <pre class="brush: javascript noskimlinks noskimwords">methods: { playTrack(index) { this.$emit('playtrack', index) }, pauseTrack() { this.$emit('pausetrack') }, stopTrack() { this.$emit('stoptrack') } }</pre> <p>Now, in the <strong>Player.vue</strong> file, import, register, and add the component in the template. Then, register the event listeners (<code class="inline">@playtrack="play"</code>, <code class="inline">@pausetrack="pause"</code>, <code class="inline">@stoptrack="stop"</code>).</p> <p>Next, create the <code class="inline">index: 0</code> data property, which will hold the index of the current track. Then, create a computed <code class="inline">currentTrack()</code>: </p> <pre class="brush: javascript noskimlinks noskimwords">computed: { currentTrack () { return this.playlist[this.index] } }</pre> <p>And now we can start to create the <code class="inline">play</code>, <code class="inline">pause</code>, and <code class="inline">stop</code> methods. We’ll start with the <code class="inline">play()</code> method, but before that we need to create the <code class="inline">playing: false</code> data property, which will indicate whether the track is playing or not. Add the following code for the <code class="inline">play()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">play (index) { let selectedTrackIndex = this.playlist.findIndex(track => track === this.selectedTrack) if (typeof index === 'number') { index = index } else if (this.selectedTrack) { if (this.selectedTrack != this.currentTrack) { this.stop() } index = selectedTrackIndex } else { index = this.index } let track = this.playlist[index].howl if (track.playing()) { return } else { track.play() } this.selectedTrack = this.playlist[index] this.playing = true this.index = index }</pre> <p>The method takes an index as the parameter, which specifies the track to be played. First, we get the index of the selected track. Then, we make some checks to determine the value of the <code class="inline">index</code>. If an index is provided as an argument and it’s a number, then we use it. If a track is selected, we use the index of the selected track. If the selected track is different from the current one, we use the <code class="inline">stop()</code> method to stop the current one. Finally, if neither an index argument is passed nor a track is selected, we use the value of the <code class="inline">index</code> data property.</p> <p>Next, we get the howl (based on the index value) for the track and check whether it is playing. If it is, we return nothing; if it’s not, we play it. </p> <p>Finally, we update the <code class="inline">selectedTrack</code>, <code class="inline">playing</code> and <code class="inline">index</code> data properties.</p> <p>Let’s now create the <code class="inline">pause()</code> and <code class="inline">stop()</code> methods. </p> <pre class="brush: javascript noskimlinks noskimwords">pause () { this.currentTrack.howl.pause() this.playing = false }, stop () { this.currentTrack.howl.stop() this.playing = false }</pre> <p>Here, we just pause or stop the current track and update the <code class="inline">playing</code> data property.</p> <p>Let’s also make a track start playing on double click.</p> <p>Add <code class="inline">@dblclick="playTrack()"</code> to <code class="inline"></code> in the <strong>PlayerPlaylistPanel.vue</strong> and create the <code class="inline">playTrack()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">playTrack(index) { this.$emit('playtrack', index) }</pre> <p>Register the listener <code class="inline">@playtrack="play"</code> in the <strong>Player.vue</strong> file and voila.</p> <h3>Add the Previous and Next Buttons</h3> <p>Let’s now add the previous and next buttons.</p> <pre class="brush: html noskimlinks noskimwords"> skip_previous <!-- stop, play, and pause buttons are here --> skip_next </pre> <p>Create the <code class="inline">skipTrack()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">skipTrack (direction) { this.$emit('skiptrack', direction) }</pre> <p>Register the event listener (<code class="inline">@skiptrack="skip"</code>) in <strong>Player.vue</strong>.</p> <p>And create the <code class="inline">skip()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">skip (direction) { let index = 0 if (direction === "next") { index = this.index + 1 if (index >= this.playlist.length) { index = 0 } } else { index = this.index - 1 if (index < 0) { index = this.playlist.length - 1 } } this.skipTo(index) }, skipTo (index) { if (this.currentTrack) { this.currentTrack.howl.stop() } this.play(index) }</pre> <p>We first check if the direction is <code class="inline">next</code>. If so, we increment the index by 1. And if the index gets bigger than the last index in the array, then we start again from zero. When the direction is <code class="inline">prev</code>, we decrement the index by 1. And if the index is less than zero, then we use the last index. At the end, we use the <code class="inline">index</code> as an argument for the <code class="inline">skipTo()</code> method. It stops the current track and plays the next or previous.</p> <p>Here is how the player looks with the buttons:</p> <figure class="post_image"><img alt="The player play buttons"></figure> <h3>Add the Volume Slider<br /> </h3> <p>Add the following before all the buttons:</p> <pre class="brush: html noskimlinks noskimwords"></pre> <p>Here, we use the Vuetify <a href="https://vuetifyjs.com/en/components/sliders" target="_self">slider</a> component.</p> <p>Add the <code class="inline">volume: 0.5</code> data property, and then create the <code class="inline">updateVolume()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">updateVolume (volume) { Howler.volume(volume) }</pre> <p>Here, we use the global Howler object to set the volume globally for all howls.</p> <p>Also, we need to sync the initial Howler volume, which by default is set to 1, to the <code class="inline">volume</code> property. If you don’t do it, the volume will show 0.5 but will be 1 initially. To do that, we’ll use the <code class="inline">created</code> hook again:</p> <pre class="brush: javascript noskimlinks noskimwords">created: function () { Howler.volume(this.volume) }</pre> <p>We want to see the volume level as a percentage on the right of the volume slider, so we add this in the template: <code class="inline">{{this.volume * 100 + '%'}}</code> </p> <h3>Add the Mute Button </h3> <p>Now, we add a volume icon before the slider. </p> <pre class="brush: html noskimlinks noskimwords"> = 0.5">volume_up 0">volume_down volume_mute volume_off </pre> <p>The icon will change according to the values of the <code class="inline">volume</code> and <code class="inline">muted</code> properties.</p> <p>Add the <code class="inline">muted: false</code> data property and create the <code class="inline">toggleMute()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">toggleMute () { Howler.mute(!this.muted) this.muted = !this.muted }</pre> <p>We use the global Howler object again to set the mute globally, and then we toggle the <code class="inline">muted</code> value. </p> <p>In the screenshot below, you can see how the volume slider should look:</p> <figure class="post_image"><img alt="The player volume slider"></figure> <h3>Add the Repeat Button</h3> <p>Add the following after all the buttons:</p> <pre class="brush: html noskimlinks noskimwords"> repeat_one repeat_one </pre> <p>Add the <code class="inline">loop: false</code> property in <strong>Player.vue</strong>, bind it <code class="inline">:loop="loop"</code> and pass the prop (<code class="inline">loop: Boolean</code>) in <strong>PlayerControlsBars.vue</strong>. </p> <p>Now, let’s create the <code class="inline">toggleLoop()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">toggleLoop () { this.$emit('toggleloop', !this.loop) }</pre> <p>Now, back in <strong>Player.vue</strong>, register the event listener (<code class="inline">@toggleloop="toggleLoop"</code>) and create the <code class="inline">toggleLoop()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">toggleLoop (value) { this.loop = value }</pre> <p>At this point, we face a small problem. When a track seeks the end, it just stops. The player doesn’t move to the next track, nor does it repeat the current track. To fix that, we need to add the following to the <code class="inline">created</code> function after the <code class="inline">src</code> property:</p> <pre class="brush: javascript noskimlinks noskimwords">onend: () => { if (this.loop) { this.play(this.index) } else { this.skip('next') } }</pre> <p>Now, when the <code class="inline">loop</code> is on, the current track will be repeated. If it’s off, the player will move on the next track.</p> <h3>Add the Shuffle Button</h3> <p>Add the following after the repeat button:</p> <pre class="brush: html noskimlinks noskimwords"> shuffle shuffle </pre> <p>Add the <code class="inline">shuffle: false</code> property in <code class="inline">Player.vue</code>, bind it (<code class="inline">:shuffle="shuffle"</code>), and pass the prop (<code class="inline">shuffle: Boolean</code>) in <code class="inline">PlayerControlsBars.vue</code>. </p> <p>Now, let’s create the <code class="inline">toggleShuffle()</code> method;</p> <pre class="brush: javascript noskimlinks noskimwords">toggleShuffle () { this.$emit('toggleshuffle', !this.shuffle) }</pre> <p>Now, back in <strong>Player.vue</strong>, register the event listener (<code class="inline">@toggleshuffle="toggleShuffle"</code>) and create the <code class="inline">toggleShuffle()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">toggleShuffle (value) { this.shuffle = value }</pre> <p>Now, add the following to the <code class="inline">skip()</code> method after <code class="inline">index = 0</code>:</p> <pre class="brush: javascript noskimlinks noskimwords">lastIndex = this.playlist.length - 1 if (this.shuffle) { index = Math.round(Math.random() * lastIndex) while (index === this.index) { index = Math.round(Math.random() * lastIndex) } } else if (direction === "next") { ...</pre> <p>Here’s how your app should look now:</p> <figure class="post_image"><img alt="The player repeat and shuffle buttons"></figure> <h3>Add the Seek Bar</h3> <p>First, in <strong>Player.vue</strong>, create the <code class="inline">seek: 0</code> property. Then we’ll need to watch the <code class="inline">playing</code> property in order to update the seek.</p> <pre class="brush: javascript noskimlinks noskimwords">watch: { playing(playing) { this.seek = this.currentTrack.howl.seek() let updateSeek if (playing) { updateSeek = setInterval(() => { this.seek = this.currentTrack.howl.seek() }, 250) } else { clearInterval(updateSeek) } }, }</pre> <p>This will update the seek value four times per second.</p> <p>Now, create a computed <code class="inline">progress()</code>:</p> <pre class="brush: javascript noskimlinks noskimwords">progress () { if (this.currentTrack.howl.duration() === 0) return 0 return this.seek / this.currentTrack.howl.duration() }</pre> <p>Bind it (<code class="inline">:progress="progress"</code>) in the template. </p> <p>Now, in <strong>PlayerControlsBars.vue</strong>, pass the <code class="inline">progress</code> prop (<code class="inline">progress: Number</code>) and add another toolbar below the one we’ve already created:</p> <pre class="brush: html noskimlinks noskimwords"> </pre> <p>Here, we use the Vuetify <a href="https://vuetifyjs.com/en/components/progress" target="_self">progress</a> component.</p> <p>Create a computed <code class="inline">trackProgress()</code>, which will get the track’s progress as a percentage.</p> <pre class="brush: javascript noskimlinks noskimwords">computed: { trackProgress () { return this.progress * 100 }, }</pre> <p>And now, create the <code class="inline">updateSeek()</code> method: </p> <pre class="brush: javascript noskimlinks noskimwords">updateSeek (event) { let el = document.querySelector(".progress-linear__bar"), mousePos = event.offsetX, elWidth = el.clientWidth, percents = (mousePos / elWidth) * 100 this.$emit('updateseek', percents) } </pre> <p>Here, we get the progress bar element, which uses the <code class="inline">.progress-linear__bar</code> class. I found this with the Browser DevTools. Next, we get the mouse position and the width of the bar. Then, we get the mouse click position as a percentage.</p> <p>Back in <strong>Player.vue</strong>, add and register the event listener (<code class="inline">@updateseek="setSeek"</code>) and create the <code class="inline">setSeek()</code> method:</p> <pre class="brush: javascript noskimlinks noskimwords">setSeek (percents) { let track = this.currentTrack.howl if (track.playing()) { track.seek((track.duration() / 100) * percents) } }</pre> <p>And boom! You can use your mouse to change the position of the played track.</p> <h2>Build the Info Panel Component</h2> <p>Create the <strong>PlayerInfoPanel.vue</strong> file with the following content:</p> <pre class="brush: html noskimlinks noskimwords"> <h2>{{ trackInfo.artist }} - {{ trackInfo.title }}</h2> <h3>{{trackInfo.seek | minutes}}/{{trackInfo.duration | minutes}}</h3> export default { props: { trackInfo: Object }, } </pre> <p>Here, we pass a prop <code class="inline">trackInfo</code>, which we use to populate the track information in our component.</p> <p>Now, back in <strong>Player.vue</strong>, import, register and add the component in the template. </p> <p>Then, create a computed <code class="inline">getTrackInfo()</code>: </p> <pre class="brush: javascript noskimlinks noskimwords">getTrackInfo () { let artist = this.currentTrack.artist, title = this.currentTrack.title, seek = this.seek, duration = this.currentTrack.howl.duration() return { artist, title, seek, duration, } }</pre> <p>Next, we bind it in the template (<code class="inline">:trackInfo="getTrackInfo"</code>) and voila. We get some basic info for the currently played track, as you can see in the screenshot below.</p> <figure class="post_image"><img alt="The player info panel"></figure> <h2>Build the Search Bar Component</h2> <p>Create the <strong>PlayerSearchBar.vue</strong> file with the following content: </p> <pre class="brush: html noskimlinks noskimwords"> export default { props: { playlist: Array }, data () { return { searchString: "", } }, methods: { searchPlaylist () { this.playlist.forEach((track) => { if (this.searchString) { if (!track.title.toLowerCase().includes(this.searchString.toLowerCase()) && !track.artist.toLowerCase().includes(this.searchString.toLowerCase())) { track.display = false } else { track.display = true } } else if (this.searchString === "" || this.searchString === null) { track.display = true } }) } }, } </pre> <p>We create a <a href="https://vuetifyjs.com/en/components/text-fields" target="_self">text field</a> and add the <code class="inline">clearable</code> prop to show a clearing icon when we type something.</p> <p>By using <code class="inline">v-model</code>, we bind it to the <code class="inline">searchString</code>, which is an empty string initially. And we add an input event listener.</p> <p>We also pass the <code class="inline">playlist</code> prop, which we use in the <code class="inline">searchPlaylist()</code> method. In this method, we use the <code class="inline">display</code> property and turn it <code class="inline">off</code> for each track where the title or artist doesn’t match the search string, and we keep it or turn it <code class="inline">on</code> for all matches. Finally, if the search string is empty or equal to <code class="inline">null</code>, which happens when we clear the field with the clear button, we turn <code class="inline">on</code> the <code class="inline">display</code> for all tracks.</p> <p>Now, back in <strong>Player.vue</strong>, import, register and add the component in the template. </p> <p>Bind the playlist property (<code class="inline">:playlist="playlist"</code>) and check the functionality. Here is how it should look in action:</p> <figure class="post_image"><img alt="The player search bar test"></figure> <h2>Some Improvement Ideas</h2> <p>As you can see, with a clear goal and proper planning, building a Vue/Vuetify app can be really easy and enjoyable. You now have a working music player which you can use during your relaxation or coding time. Of course, there is always room for further improvements and additions, so here are some ideas you can try out to make the player even more feature-rich: </p> <ul> <li>multiple playlist support</li> <li>ability to add or remove tracks from the playlist</li> <li>drag-and-drop support</li> <li>ability to sort the tracks</li> <li>audio visualization </li> </ul> <h2>Conclusion</h2> <p>In this tutorial, we saw how easy and enjoyable it can be to build an app with Vue.js, and with Vuetify.js in particular. I hope you enjoyed building this player as much as I did. I’ll be glad to see your own improved version of the player. So if you create one, just drop a demo link in the comments!</p> <div class="mediafed_ad"><img border="0" height="1" src="http://audio.tutsplus.com.feedsportal.com/c/35227/f/668806/s/31228/sc/4/mf.gif" width="1" /><a href="http://da.feedsportal.com/r/186529796139/u/407/f/668806/c/35227/s/31228/a2.htm"><img border="0" src="http://da.feedsportal.com/r/186529796139/u/407/f/668806/c/35227/s/31228/a2.img" /></a><img border="0" height="1" src="http://pi.feedsportal.com/r/186529796139/u/407/f/668806/c/35227/s/31228/a2t.img" width="1" /></div> <p><a href="https://code.tutsplus.com/tutorials/building-a-full-featured-music-player-with-vuetify--cms-31228" rel="nofollow" target="_blank">Link To Source</a></p> </div> </div> <div class="post-footer"> <div class="social-icons ed-social-share-icons"> <p class="share-title">Share:</p> <a href="http://www.facebook.com/sharer.php?u=http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/&t=Build%20a%20Music%20Player%20With%20Vuetify" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Facebook" data-network-name="facebook" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-facebook et-extra-icon-background-hover" ></span> </a><a href="http://twitter.com/home?status=Build%20a%20Music%20Player%20With%20Vuetify%20http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Twitter" data-network-name="twitter" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-twitter et-extra-icon-background-hover" ></span> </a><a href="https://plus.google.com/share?url=http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/&t=Build%20a%20Music%20Player%20With%20Vuetify" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Google +" data-network-name="googleplus" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-googleplus et-extra-icon-background-hover" ></span> </a><a href="https://www.tumblr.com/share?v=3&u=http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/&t=Build%20a%20Music%20Player%20With%20Vuetify" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Tumblr" data-network-name="tumblr" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-tumblr et-extra-icon-background-hover" ></span> </a><a href="http://www.pinterest.com/pin/create/button/?url=http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/&description=Build%20a%20Music%20Player%20With%20Vuetify&media=" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Pinterest" data-network-name="pinterest" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-pinterest et-extra-icon-background-hover" ></span> </a><a href="http://www.linkedin.com/shareArticle?mini=true&url=http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/&title=Build%20a%20Music%20Player%20With%20Vuetify" class="social-share-link" title="Share "Build a Music Player With Vuetify" via LinkedIn" data-network-name="linkedin" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-linkedin et-extra-icon-background-hover" ></span> </a><a href="https://bufferapp.com/add?url=http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/&title=Build%20a%20Music%20Player%20With%20Vuetify" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Buffer" data-network-name="buffer" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-buffer et-extra-icon-background-hover" ></span> </a><a href="http://www.stumbleupon.com/badge?url=http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/&title=Build%20a%20Music%20Player%20With%20Vuetify" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Stumbleupon" data-network-name="stumbleupon" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-stumbleupon et-extra-icon-background-hover" ></span> </a><a href="#" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Email" data-network-name="basic_email" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-basic_email et-extra-icon-background-hover" ></span> </a><a href="#" class="social-share-link" title="Share "Build a Music Player With Vuetify" via Print" data-network-name="basic_print" data-share-title="Build a Music Player With Vuetify" data-share-url="http://gracemanaay.tk/2018/07/12/build-a-music-player-with-vuetify/"> <span class="et-extra-icon et-extra-icon-basic_print et-extra-icon-background-hover" ></span> </a> </div> <div class="rating-stars"> <p id="rate-title" class="rate-title">Rate:</p><div id="rating-stars"></div><input type="hidden" id="post_id" value="1853" /> </div> <style type="text/css" id="rating-stars"> .post-footer .rating-stars #rated-stars img.star-on, .post-footer .rating-stars #rating-stars img.star-on { background-color: #7464f2; } </style> </div> <div class="et_pb_extra_row etad post_below"> <div class="adsense-responsive-ad"><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <!-- Ads 1 --> <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-1299274443223272" data-ad-slot="9142535381" data-ad-format="auto"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script></div> </div> </article> <nav class="post-nav"> <div class="nav-links clearfix"> <div class="nav-link nav-link-prev"> <a href="http://gracemanaay.tk/2018/07/11/unused/" rel="prev"><span class="button">Previous</span><span class="title">Unused</span></a> </div> <div class="nav-link nav-link-next"> <a href="http://gracemanaay.tk/2018/07/12/design-systems-at-github/" rel="next"><span class="button">Next</span><span class="title">Design Systems at GitHub</span></a> </div> </div> </nav> <div class="et_extra_other_module author-box vcard"> <div class="author-box-header"> <h3>About The Author</h3> </div> <div class="author-box-content clearfix"> <div class="author-box-avatar"> <img alt='admin' src='http://2.gravatar.com/avatar/257188b3a69a55a508edb68808648625?s=170&d=mm&r=g' srcset='http://2.gravatar.com/avatar/257188b3a69a55a508edb68808648625?s=340&d=mm&r=g 2x' class='avatar avatar-170 photo' height='170' width='170' /> </div> <div class="author-box-description"> <h4><a class="author-link url fn" href="http://gracemanaay.tk/author/admin/" rel="author" title="View all posts by admin">admin</a></h4> <p class="note"></p> <ul class="social-icons"> </ul> </div> </div> </div> <div class="et_extra_other_module related-posts"> <div class="related-posts-header"> <h3>Related Posts</h3> </div> <div class="related-posts-content clearfix"> <div class="related-post"> <div class="featured-image"><a href="http://gracemanaay.tk/2018/06/08/set-up-an-oauth2-server-using-passport-in-laravel/" title="Set Up an OAuth2 Server Using Passport in Laravel" class="post-thumbnail"> <img src="http://audio.tutsplus.com.feedsportal.com/c/35227/f/668806/s/30576/sc/4/mf.gif" alt="Set Up an OAuth2 Server Using Passport in Laravel" /><span class="et_pb_extra_overlay"></span> </a></div> <h4 class="title"><a href="http://gracemanaay.tk/2018/06/08/set-up-an-oauth2-server-using-passport-in-laravel/">Set Up an OAuth2 Server Using Passport in Laravel</a></h4> <p class="date"><span class="updated">June 8, 2018</span></p> </div> <div class="related-post"> <div class="featured-image"><a href="http://gracemanaay.tk/2018/01/12/how-to-auto-update-wordpress-salts/" title="How to Auto Update WordPress Salts" class="post-thumbnail"> <img src="https://cms-assets.tutsplus.com/uploads/users/1527/posts/30014/image/1-defaul-wp-salts.png" alt="How to Auto Update WordPress Salts" /><span class="et_pb_extra_overlay"></span> </a></div> <h4 class="title"><a href="http://gracemanaay.tk/2018/01/12/how-to-auto-update-wordpress-salts/">How to Auto Update WordPress Salts</a></h4> <p class="date"><span class="updated">January 12, 2018</span></p> </div> <div class="related-post"> <div class="featured-image"><a href="http://gracemanaay.tk/2018/01/24/get-more-installs-and-better-ratings-for-your-apps-using-instabug/" title="Get More Installs and Better Ratings for Your Apps Using Instabug" class="post-thumbnail"> <img src="https://cms-assets.tutsplus.com/uploads/users/1251/posts/30215/image/instabug_usertags.jpg" alt="Get More Installs and Better Ratings for Your Apps Using Instabug" /><span class="et_pb_extra_overlay"></span> </a></div> <h4 class="title"><a href="http://gracemanaay.tk/2018/01/24/get-more-installs-and-better-ratings-for-your-apps-using-instabug/">Get More Installs and Better Ratings for Your Apps Using Instabug</a></h4> <p class="date"><span class="updated">January 24, 2018</span></p> </div> <div class="related-post"> <div class="featured-image"><a href="http://gracemanaay.tk/2017/11/30/new-wordpress-course-adding-hooks-to-your-themes/" title="New WordPress Course: Adding Hooks to Your Themes" class="post-thumbnail"> <img src="https://cms-assets.tutsplus.com/uploads/users/48/posts/30039/image/WP-hooks.jpg" alt="New WordPress Course: Adding Hooks to Your Themes" /><span class="et_pb_extra_overlay"></span> </a></div> <h4 class="title"><a href="http://gracemanaay.tk/2017/11/30/new-wordpress-course-adding-hooks-to-your-themes/">New WordPress Course: Adding Hooks to Your Themes</a></h4> <p class="date"><span class="updated">November 30, 2017</span></p> </div> </div> </div> <!-- You can start editing here. --> <section id="comment-wrap"> <div id="comments" class="nocomments"> <!-- If comments are open, but there are no comments. --> </div> <div id="respond" class="comment-respond"> <h3 id="reply-title" class="comment-reply-title"><span>Leave a reply</span> <small><a rel="nofollow" id="cancel-comment-reply-link" href="/2018/07/12/build-a-music-player-with-vuetify/#respond" style="display:none;">Cancel reply</a></small></h3> <form action="http://www.gracemanaay.tk/wp-comments-post.php" method="post" id="commentform" class="comment-form"> <p class="comment-notes"><span id="email-notes">Your email address will not be published.</span> Required fields are marked <span class="required">*</span></p><p class="comment-form-comment"><label for="comment">COMMENT</label><textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea></p><p class="comment-form-author"><label for="author">Name <span class="required">*</span></label> <input id="author" name="author" type="text" value="" size="30" maxlength="245" aria-required='true' required='required' /></p> <p class="comment-form-email"><label for="email">Email <span class="required">*</span></label> <input id="email" name="email" type="text" value="" size="30" maxlength="100" aria-describedby="email-notes" aria-required='true' required='required' /></p> <p class="comment-form-url"><label for="url">Website</label> <input id="url" name="url" type="text" value="" size="30" maxlength="200" /></p> <p class="form-submit"><input name="submit" type="submit" id="submit" class="submit" value="Post Comment" /> <input type='hidden' name='comment_post_ID' value='1853' id='comment_post_ID' /> <input type='hidden' name='comment_parent' id='comment_parent' value='0' /> </p> </form> </div><!-- #respond --> </section> </div><!-- /.et_pb_extra_column.et_pb_extra_column_main --> <div class="et_pb_extra_column_sidebar"> <div id="search-2" class="et_pb_widget widget_search"><h4 class="widgettitle"> </h4><form role="search" method="get" class="search-form" action="http://gracemanaay.tk/"> <label> <span class="screen-reader-text">Search for:</span> <input type="search" class="search-field" placeholder="Search …" value="" name="s" /> </label> <input type="submit" class="search-submit" value="Search" /> </form></div> <!-- end .et_pb_widget --> <div id="recent-posts-2" class="et_pb_widget widget_recent_entries"> <h4 class="widgettitle">Recent Posts</h4> <ul> <li> <a href="http://gracemanaay.tk/2018/07/21/your-body-text-is-too-small/">Your Body Text is Too Small</a> </li> <li> <a href="http://gracemanaay.tk/2018/07/20/how-to-upload-and-download-csv-files-with-angularjs/">How to Upload and Download CSV Files With AngularJS</a> </li> <li> <a href="http://gracemanaay.tk/2018/07/19/css-a-new-kind-of-javascript/">CSS: A New Kind of JavaScript</a> </li> <li> <a href="http://gracemanaay.tk/2018/07/18/best-wordpress-widgets-for-2018/">Best WordPress Widgets for 2018</a> </li> <li> <a href="http://gracemanaay.tk/2018/07/18/what-bit-of-advice-would-you-share-with-someone-new-to-your-field/">What bit of advice would you share with someone new to your field?</a> </li> </ul> </div> <!-- end .et_pb_widget --> <div id="et_social_followers-2" class="et_pb_widget widget_et_social_followers"><h4 class="widgettitle">Follow Us</h4> <div class="widget_content"> <ul class="widget_list"> <li> <a class="et-extra-icon et-extra-icon-facebook et-extra-icon-background social-icon" href="https://www.facebook.com/asensoBrgy2WomensSilay"></a> <a href="https://www.facebook.com/asensoBrgy2WomensSilay" class="widget_list_social"> <h3 class="title">Facebook</h3> </a> </li> <li> <a class="et-extra-icon et-extra-icon-googleplus et-extra-icon-background social-icon" href="https://plus.google.com/u/0/109536858712215471785"></a> <a href="https://plus.google.com/u/0/109536858712215471785" class="widget_list_social"> <h3 class="title">Google+</h3> </a> </li> <li> <a class="et-extra-icon et-extra-icon-youtube et-extra-icon-background social-icon" href="https://www.youtube.com/user/gracemanaay"></a> <a href="https://www.youtube.com/user/gracemanaay" class="widget_list_social"> <h3 class="title">Youtube</h3> </a> </li> <li> <a class="et-extra-icon et-extra-icon-twitter et-extra-icon-background social-icon" href="https://twitter.com/twittwitadik"></a> <a href="https://twitter.com/twittwitadik" class="widget_list_social"> <h3 class="title">Twitter</h3> </a> </li> <li> <a class="et-extra-icon et-extra-icon-instagram et-extra-icon-background social-icon" href="https://www.instagram.com/deez.grace/"></a> <a href="https://www.instagram.com/deez.grace/" class="widget_list_social"> <h3 class="title">Instagram</h3> </a> </li> </ul> </div> </div> <!-- end .et_pb_widget --></div> </div> <!-- #content-area --> </div> <!-- .container --> </div> <!-- #main-content --> <div class="container"> <div class="et_pb_extra_row etad footer_above"> <div class="adsense-responsive-ad"><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <!-- Ads 1 --> <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-1299274443223272" data-ad-slot="9142535381" data-ad-format="auto"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script></div> </div> </div> <footer id="footer" class="footer_columns_3"> <div id="footer-bottom"> <div class="container"> <!-- Footer Info --> <p id="footer-info">Designed by <a href="http://www.elegantthemes.com" title="Premium WordPress Themes">Elegant Themes</a> | Powered by <a href="http://www.wordpress.org">WordPress</a></p> <!-- Footer Navigation --> <div id="footer-nav"> <ul class="et-extra-social-icons" style=""> <li class="et-extra-social-icon facebook"> <a href="https://www.facebook.com/grace.manaay" class="et-extra-icon et-extra-icon-background-none et-extra-icon-facebook"></a> </li> <li class="et-extra-social-icon twitter"> <a href="https://twitter.com/twittwitadik" class="et-extra-icon et-extra-icon-background-none et-extra-icon-twitter"></a> </li> <li class="et-extra-social-icon googleplus"> <a href="https://plus.google.com/u/0/109536858712215471785" class="et-extra-icon et-extra-icon-background-none et-extra-icon-googleplus"></a> </li> <li class="et-extra-social-icon instagram"> <a href="https://www.instagram.com/deez.grace/" class="et-extra-icon et-extra-icon-background-none et-extra-icon-instagram"></a> </li> <li class="et-extra-social-icon youtube"> <a href="https://www.youtube.com/user/gracemanaay" class="et-extra-icon et-extra-icon-background-none et-extra-icon-youtube"></a> </li> </ul> </div> <!-- /#et-footer-nav --> </div> </div> </footer> </div> <!-- #page-container --> <span title="Back To Top" id="back_to_top"></span> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-15295893-6', 'auto'); ga('send', 'pageview'); </script><script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/includes/builder/scripts/frontend-builder-global-functions.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-includes/js/imagesloaded.min.js?ver=3.2.0'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/scripts/ext/jquery.waypoints.min.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/scripts/ext/jquery.fitvids.min.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/scripts/ext/smoothscroll.min.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-includes/js/masonry.min.js?ver=3.3.2'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/includes/builder/scripts/salvattore.min.js?ver=2.0.61'></script> <script type='text/javascript'> /* <![CDATA[ */ var EXTRA = {"images_uri":"http:\/\/www.gracemanaay.tk\/wp-content\/themes\/Extra\/images\/","ajaxurl":"http:\/\/www.gracemanaay.tk\/wp-admin\/admin-ajax.php","your_rating":"Your Rating:","item_in_cart_count":"%d Item in Cart","items_in_cart_count":"%d Items in Cart","item_count":"%d Item","items_count":"%d Items","rating_nonce":"6dfd753c2c","timeline_nonce":"2d80f5fa5e","blog_feed_nonce":"8c822bea0b","error":"There was a problem, please try again.","contact_error_name_required":"Name field cannot be empty.","contact_error_email_required":"Email field cannot be empty.","contact_error_email_invalid":"Please enter a valid email address.","is_ab_testing_active":"","is_cache_plugin_active":"no"}; /* ]]> */ </script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/scripts/scripts.min.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-includes/js/comment-reply.min.js?ver=4.8.7'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/scripts/ext/jquery.raty.min.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/includes/builder/scripts/jquery.fitvids.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/includes/builder/scripts/jquery.magnific-popup.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/includes/builder/scripts/jquery.mobile.custom.min.js?ver=2.0.61'></script> <script type='text/javascript'> /* <![CDATA[ */ var et_pb_custom = {"ajaxurl":"http:\/\/www.gracemanaay.tk\/wp-admin\/admin-ajax.php","images_uri":"http:\/\/www.gracemanaay.tk\/wp-content\/themes\/Extra\/images","builder_images_uri":"http:\/\/www.gracemanaay.tk\/wp-content\/themes\/Extra\/includes\/builder\/images","et_frontend_nonce":"b5558ab048","subscription_failed":"Please, check the fields below to make sure you entered the correct information.","et_ab_log_nonce":"7d95e5a8c4","fill_message":"Please, fill in the following fields:","contact_error_message":"Please, fix the following errors:","invalid":"Invalid email","captcha":"Captcha","prev":"Prev","previous":"Previous","next":"Next","wrong_captcha":"You entered the wrong number in captcha.","is_builder_plugin_used":"","ignore_waypoints":"no","is_divi_theme_used":"","widget_search_selector":".widget_search","is_ab_testing_active":"","page_id":"1853","unique_test_id":"","ab_bounce_rate":"5","is_cache_plugin_active":"no","is_shortcode_tracking":""}; /* ]]> */ </script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-content/themes/Extra/includes/builder/scripts/frontend-builder-scripts.js?ver=2.0.61'></script> <script type='text/javascript' src='http://www.gracemanaay.tk/wp-includes/js/wp-embed.min.js?ver=4.8.7'></script> <div style='text-align: right;position: fixed;z-index:9999999;bottom: 0; width: 100%;cursor: pointer;line-height: 0;display:block !important;'><a title="000webhost logo" rel="nofollow" target="_blank" href="https://www.000webhost.com/?utm_source=000webhostapp&utm_campaign=000_logo&utm_campaign=ss-footer_logo&utm_medium=000_logo&utm_content=website"><img src="https://cdn.rawgit.com/000webhost/logo/e9bd13f7/footer-powered-by-000webhost-white2.png" alt="000webhost logo"></a></div></body> </html>