Breaking

Showing posts with label Javascript. Show all posts
Showing posts with label Javascript. Show all posts

Wednesday, November 22, 2017

November 22, 2017

Cara Mudah Menambahkan Tombol Pin It di Gambar

Pernahkah sobat bertanya-tanya bagaimana cara menambahkan tombol Pin It Pinterest di postingan blog? Nah, di sini Arlina Design akan berbagi Cara Mudah Menambahkan Tombol Pin It di Gambar.

Cara Mudah Menambahkan Tombol Pin It di Gambar

Fungsi dari tombol ini untuk memnagikan secara langsung gambar yang ada di blog sobat ke akun Pinterest sobat. Cara kerja dari Tombol share Pin It ini yaitu tombol akan muncul saat sobat memfokuskan kursor pada setiap gambar di dalam blog.
Selengkapnya »

Tuesday, September 19, 2017

September 19, 2017

Cara Otomatis Menambahkan Link Sumber

Cara Otomatis Menambahkan Link Sumber - Di Internet sangat mudah untuk menyalin teks dari satu situs dan menempelkannya ke situs lain. Secara teknis, jika pengunjung blog menyalin teks dari situs tertentu seharusnya dia harus memberikan link ke sumber dari situs yang ia salin teksnya. Namun ada juga orang yang mengabaikan hal penting ini.

Cara Otomatis Menambahkan Link Sumber

Untuk menghindari hal itu, di sini saya akan memberikan sebuah kode yang secara otomatis jika seseorang menyalin bagian teks dari artikel kita maka akan otomatis memunculkan link sumber dari teks yang ia salin.
Selengkapnya »

Thursday, March 9, 2017

March 09, 2017

Mengenal Node.js

Pada kesempatan kali ini saya akan membahas tentang Node.js dan dipostingan kali ini Anda dapat belajar untuk mulai menggunakan Node.js. Sebelum memulai ada baiknya anda mengetahui terlebih dahulu tentang apa itu Node.js .

Apa itu Node.js ?

Node.js merupakan runtime javascript opensource yang dibuat berdasarkan dari Google Chrome V8 engine untuk mengembangkan berbagai macam alat atau aplikasi sisi server. Meskipun Node.js bukan merupakan framework JavaScript, namun kebanyakan modul dasar yang ada di Node.js ditulis menggunakan JavaScript, dan pengembang dapat menulis modul baru menggunakan JavaScript. 
Node.js menggunakan event-driven dan non-blockin I/O model yang membuatnya ringan dan efisien.
Dengan kata lain Node.js menawarkan kemungkinan untuk membuat server menggunakan bahasa pemrograman javascript dengan kinerja yang luar biasa.
Aplikasi Node.js dapat berjalan di MacOS, Microsoft Windows, NonStop, dan server Unix. Aplikasi Node.js dapat ditulis dengan CoffeeScript (alternatif JavaScript), Dart atau Microsoft TypeScript, atau bahasa lain yang dapat mengkompilasi ke JavaScript. Node.js umumnya digunakan untuk membangun program jaringan seperti web server. Perbedaan terbesar antara Node.js dan PHP adalah bahwa sebagian besar fungsi dalam blok PHP sampai selesai (perintah dieksekusi hanya setelah perintah sebelumnya telah selesai), sementara fungsi dalam Node.js dirancang untuk menjadi non-blocking (perintah mengeksekusi secara paralel, dan penggunaan callback sinyal selesai atau kegagalan).

Menginstall Node.js

Sebelum memulai menggunakan Node.js Anda perlu menginstall Node.js runtime yang dapat Anda download pada halaman web resmi Node.js : https://nodejs.org/en/download/
Untuk mulai menggunakan Node.js mari mencobanya terlebih dahulu melalui terminal/cmd dengan mengetikan perintah node pada termminal/cmd


Sekarang  mari kita mencoba untuk menampilkan Hello World! pada terminal/cmd dengan mengetikkan perintah berikut

console.log('Hello World!')

Nantinya outputnya akan menjadi seperti gambar berikut:


Sekarang kita akan coba untuk membuat aplikasi Command Line Interface yang melakukan perhitungan sederhana seperti penambahan dan pengurangan. Buatlah file javascript baru dan beri nama index.js.
Untuk melakukan penambahan dan pengurangan kita akan memberikan argumen ke aplikasi, jika pengurangan maka argumen yang diberikan contohnya adalah 2 - 2, dan jika penambahan maka contonya adalah 2 + 2.
Sekarang kita perlu mendapatkan 3 argumen tersebut, untuk mendapatkan 3 argumen tersebut ketikkan script berikut ke file javascript yang baru saja Anda buat.

process.title = 'duniainternet27';
// mendapatkan semua argumen setelah nama file
let args = process.argv.slice(2);

Pada script diatas saya memberikan judul proses dengan menggunakan process.title, judul ini nantinya hanya akan tampil ketika proses sedang berlangsung. Kemudian pada baris selanjutnya saya menggunakan process.argsv untuk mendapatkan semua argumen,  process.argsv memberikan nilai dalam bentuk Array, jadi kita dapat menggunakan slice untuk mendapatkan salinan dari array tanpa merubah array aslinya. Angka 2 merupakan index array dimana terdapat argumen yang kita butuhkan yaitu angka pertama, jadi slice akan menyalin array dimulai dari index ke 2 hingga terakhir.

Sekarang kita perlu mengecek simbol aritmatik yang ada pada index 1 di variable args kemudian melakukan pengurangan atau penambahan tergantung dari simbol aritmatik yang diberikan. Perlu diingat bahwa semua nilai yang diberikan oleh process.argv merupakan tipe data primitif string, sehingga Anda perlu melakukan parsing untuk dapat melakukan perhitungan antara angka pertama dan angka kedua.

if (args[1] == '+') {
console.log(parseInt(args[0]) + parseInt(args[2]));
} else if (args[1] == '-') {
console.log(parseInt(args[0]) - parseInt(args[2]));
}
Sekarang Anda dapat menjalankannya menggunakan perintah node index.js 1 + 2.


Mungkin itu saja yang dapat saya share pada kesempatan kali ini.

Semoga bermanfaat ...!!


Full Code:
process.title = 'duniainternet27';
// get all arguments after our filename
let args = process.argv.slice(2);
if (args[1] == '+') {
console.log(parseInt(args[0]) + parseInt(args[2]));
} else if (args[1] == '-') {
console.log(parseInt(args[0]) - parseInt(args[2]));
}

Monday, February 6, 2017

February 06, 2017

Notifikasi Adblocker Adsense Ala Kompi Ajaib

Salam Blogger, kali ini Arlina Design akan berbagi tutorial cara memberikan notifikasi adblocker adsense saat pengguna mengakses blog dan mengaktifkan ekstensi adblock di browser.

Notifikasi Adblocker Adsense Ala Kompi Ajaib

Tutorial ini saya dapat dari blog Kompi Ajaib yang dikelola oleh admin +Adhy Suryadi yang senantiasa memberikan trik dan tips blogger yang bermanfaat dan beliau sangat menginspirasi saya untuk membangun blog menjadi lebih baik. Mengenai penjelasan kenapa sobat perlu Menambahkan Notifikasi Adblocker Adsense di dalam blog sobat bisa mengunjungi sumbernya di http://www.kompiajaib.com/2016/07/membuat-notifikasi-untuk-pengguna.html
Selengkapnya »

Tuesday, January 17, 2017

January 17, 2017

Cara Memasang Artikel Terkait di Dalam Postingan

Cara Memasang Artikel Terkait di Dalam Postingan - Halo sobat blogger, tentunya sobat sudah mengetahui dengan fungsi sebuah widget artikel terkait di blog yang menampilkan sejumlah artikel terkait sesuai label di bawah artikel baik itu dalam bentuk link dan thumbnail ataupun link saja.

Cara Memasang Artikel Terkait di Dalam Postingan

Namun sedikit berbeda dari sebelumnya, di sini saya akan memberikan tutorial Cara Memasang Artikel Terkait di Dalam Postingan. Seperti yang saya singgung sebelumnya di artikel Cara Memasang Iklan Adsense di Tengah Postingan bahwa sobat juga bisa memasang kode atau widget lain di dalam kode tersebut, tutorial blogger kali ini pun masih menggunakan kode yang sama. Tutorial ini saya dapatkan dari blog http://www.bungfrangki.com/2015/12/membuat-related-pos-dalam-postingan.html dan telah saya modifikasi kembali disesuaikan dengan style saya.
Selengkapnya »

Tuesday, January 10, 2017

January 10, 2017

Cara Memasang Iklan Adsense di Tengah Postingan

Cara Memasang Iklan Adsense di Tengah Postingan - Masih membahas seputar Adsense, kali ini Arlina Design membahas tentang trik dan tips Cara Memasang Iklan Adsense di tengah postingan. Sebetulnya sudah banyak sekali artikel yang membahas tentang trik ini, namun kebetulan di blog ini belum ada jadi saya bagikan dan mungkin judulnya agak mainstream menurut saya tapi tidak apa-apa yang penting berbagi.

Cara Memasang Iklan Adsense di Tengah Postingan

Manfaat dari trik Memasang Iklan Adsense di tengah postingan ini yaitu sobat bisa lebih meningkatkan pendapatan Google Adsense blog sobat dengan jumlah rasio klik yang lebih tinggi dan biasanya akan lebih menarik minat pembaca dengan konten iklan yang ditampilkan karena posisi iklan yang strategis berada di tengah postingan. Singkat saja, berikut Cara Memasang Iklan Adsense di Tengah Postingan.
Selengkapnya »

Monday, December 5, 2016

December 05, 2016

Pengenalan Service Worker

Javascript Service Worker


Pada beberapa postingan sebelumnya saya telah membahas tentang Javascript Promise, dan pada postingan kali ini yang akan dibahas adalah tentang Service Worker.

Apa Itu Service Worker ?

Service Worker adalah sebuah script yang dijalankan browser Anda di latar belakang, terpisah dari halaman web, membuka pintu untuk fitur yang tidak memerlukan halaman web atau interaksi pengguna. Saat ini, Service Worker sudah termasuk fitur seperti push notification dan bakground synch. Di masa yang akan datang Service Worker akan mendukung hal-hal lain seperti sinkronisasi periodik atau geofencing. Fitur inti yang dibahas dalam tutorial ini adalah kemampuan untuk mencegat dan menangani permintaan jaringan, termasuk mengelola cache.

Yang membuat Service Worker menarik adalah API ini memberikan dukungan untuk pengalaman offline kepada pengguna, memberikan kontrol penuh kepada pengambang/developer atas pengalaman pengguna.
Sebelum Service Worker ada satu API yang memberikan pengalaman offline kepada pengguna yang disebut AppCache, namun terdapat beberapa masalah pada API tersebut, Anda dapat membacanya disini. Service Worker telah dirancang untuk menghindari masalah-masalah tersebut.

Beberapa hal yang perlu Anda ketahui tentang Service Worker :

  • Service Worker adalah Javascript Worker, sehingga Anda tidak dapat berinteraksi secara langsung dengan DOM element, namun Service Worker dapat berkomunikasi dengan halaman yang di kontrol dengan menanggapi pesan yang dikirim melalui interface postMessage.
  • Service Worker memungkinkan Anda untuk mengontrol bagaimana permintaan jaringan dari halaman Anda ditangani.
  • Service Worker akan dihentikan bila sudah tdak digunakan, dan akan di restart/mulai ulang ketika dibutuhkan. Jika terdapat informasi yang ingin Ada gunakan kembali Anda dapat menggunakan IndexDB API.
  • Service Worker banyak menggunakan Promise, jadi jika Anda belum tau apa itu Promise, maka Anda harus berhenti membaca artikel ini dan bacalah artikel sebelumnya tentang Javascript Promises.
Selama pembuatan Anda akan dapat menggunakan Service Worker melalui localhost, tetapi untuk menggunakannya di situs online Anda harus memiliki setup HTTPS pada server Anda. Github Pages dilayani melalui HTTPS, jadi disana dapat menjadi tempat yang bagus untuk mencoba fitur ini.

Mendaftarkan Service Worker

Untuk menginstal Service Worker yang Anda butuhkan untuk memulai proses adalah dengan mendaftarkannya di halaman Anda. Hal ini akan memberitahu browser dimana file Javascript Service Worker Anda berada.

if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Pendaftaran berhasil
console.log('Pendaftaran ServiceWorker berhasil : ', registration.scope);
}).catch(function(err) {
// Pendaftaran gagal
console.log('Pendaftaran ServiceWorker gagal: ', err);
});
});
}
Script diatas akan memeriksa untuk melihat apakah Service Worker API tersedia, dan jika itu tersedia, Service Worker di /sw.js akan terdaftar setelah halaman dimuat.

Anda dapat memanggil fungsi register() setiap kali halaman dimuat; browser akan mencari tahu apakah Service Worker sudah terdaftar atau tidak dan menanganinya. file Service Worker harus diletakkan pada direktori root di web Anda, karena Service Worker hanya akan menerima event fetch dari folder/domain dimana file tersebut berada.
Untuk mengecek apakah Service Worker Anda telah terdaftar Anda dapat menuliskan uri berikut di browser Chrome Anda
chrome://inspect/#service-workers

Menginstall Service Worker

Setelah mendaftarkan Service Worker sekarang Anda dapat menginstall Service Worker pada halaman Anda dengan mendefinisikan callback untuk event install dan memilih apa saja yang ingin Anda cache.
self.addEventListener('install', function(event) {
// Lakukan langkah instalasi
});
Dalam callback install, kita perlu melakukan langkah-langkah berikut:

  • Membuka cache.
  • Cache file kita.
  • Mengkonfirmasi apakah semua aset perlu di cache atau tidak.

var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'./',
'./styles/main.css',
'./script/main.js'
];

self.addEventListener('install', function(event) {
// Lakukan langkah instalasi
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Cache terbuka');
return cache.addAll(urlsToCache);
})
);
});
Pada script diatas saya membuka cache dengan nama yang telah ditentukan, kemudian menambahkan file yang akan dicache dengan memasukkan array dari file yang akan dicache ke dalam fungsi cache.addAll().  metode event.waitUntill() mengambil Promise dan menggunakannya untuk menghitung jumlah waktu yang dibutuhkan dalam proses instalasi, dan apakah proses instalasi berhasil.


Wednesday, November 23, 2016

November 23, 2016

Membuat Morphing Button Menggunakan CSS & Javascript



Dalam postingan ini kita akan mencoba untuk Membuat Morphing Button Menggunakan CSS Dan Javascript, nantinya yang kita buat akan terlihat seperti musiXmatch Musik Recognizer tapi bukannya menunjukkan hasil di layar penuh, di sini saya akan menunjukkan kartu dengan inforrmasi artis dan judul lagu. Tutorial ini saya kutip langsung dari blog saya karena lagi tidak ada bahan, buat yang mengerti bahasa inggris bisa mampir ke http://muhammadsayuti.com :D.

Pertama buatlah file html baru dan beri nama index.html dan kemudian salin template di bawah ini.
<!DOCTYPE html>
<html>
<head>
<meta name="author" content="Muhammad Sayuti">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">

<link rel="stylesheet" type="text/css" href="css/style.css">
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/icons.css">
<title>Morphing Button</title>
</head>
<body>
<div class="content">
<div class="morpher">
<a class="circle-button" href="#"><span class="icon icon--microphone"></span></a>
</div>
<div class="player">
<img class="player__cover" src="img/Montevallo.jpeg" alt="Take Your Time by Sam Hunt" />
<div class="player__meta">
<h4 class="player__track">Take Your Time</h3>
<h4 class="player__artist">Sam Hunt</h3n>
<h4 class="player__album-name">Montevallo</h3>
</div>
<a href="#" class="button--close"><span class="icon icon--cross"></span></a>
</div><!-- /player -->
</div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>

Selanjutnya buatlah file css baru dan beri nama style.css dan copy css di bawah ini.
html,body{
padding:0px;
margin:0px;
font-family:san-serif;
background-color: #eee;
}
*{
box-sizing:border-box;
}

.content{
display: -ms-flexbox;
display: -webkit-flex;
display: flex;

-ms-flex-align: center;
-webkit-align-items: center;
-webkit-box-align: center;

align-items: center;
-webkit-box-pack: center;
align-items: center;
justify-content: center;
flex-direction: column;
flex-flow: column wrap;
height:100vh;
}
/* Buttton container Style */
.morpher{
color: #2196F3;
align-items: center;
background-color: #FFF;
display: flex;
-webkit-box-flex: 0;
justify-content: center;
flex: none;
min-height: 48px;
min-width: 48px;
overflow: visible;
border-radius: 25px;
transform: scale3d(1,1,1);
-webkit-transform: scale3d(1,1,1);
}
.card {
transform: scale3d(7,10,1);
-webkit-transform: scale3d(7,10,1);
border-radius: 1px;
box-shadow: 0 3px 4px rgba(0,0,0,0.3);
}
/* Buttton Style */
.btn{
color: #2196F3;
background-color: #FFF;
text-decoration: none;
display: flex;
text-align: center;
border-radius: 25px;
padding: 1em 5em;
overflow: visible;
border: none;
}
.circle-button{
width: 50px;
height:50px;
padding: 1em !important;
border-radius: 50%;
}
.listening{
position: relative;
}
.listening::before,
.listening::after {
content: '';
position: absolute;
top: calc(50% - 24px);
left: calc(50% - 24px);
height: 48px;
width: 48px;
pointer-events: none;
opacity: 1;
border: 1px solid rgba(255, 255, 255, 0.7);
border-radius: 50%;
}

.listening::before,
.listening::after {
-webkit-animation: anim-ripple 1.2s ease-out infinite forwards;
animation: anim-ripple 1.2s ease-out infinite forwards;
}
.listening::after {
-webkit-animation-delay: 0.6s;
animation-delay: 0.6s;
}

/* Player Style */
.cover{
display: none;
margin-top: -1px;
width:300px;
height:200px;
flex: none;
border-radius: 5px 5px 0 0;
}

.button--close {
position: absolute;
z-index: 10;
top: 0px;
right: 0px;
left: auto;
width: 40px;
height: 40px;
padding: 10px;
color: #eee;
pointer-events: all;
text-decoration: none;
}

.button--close:hover,
.button--close:focus {
color: #ddd;
}
.player {
position: absolute;
width:calc(50px * 7);
height:calc(50px * 10);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
pointer-events: none;
opacity: 0;
}
.player::after {
content: '';
position: absolute;
pointer-events: none;
border-radius: 5px 5px 0 0;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), transparent);
}
.player--visible {
pointer-events: none;
-webkit-transition: opacity 0.5s linear 0.2s;
transition: opacity 0.5s linear 0.2s;
opacity: 1;
}
.player__cover {
margin-top: -1px;
width:100%;
max-height: 300px;
overflow: hidden;
border-radius: 5px 5px 0 0;
}

.player__meta {
position: absolute;
width: 100%;
bottom:20px;
padding: 0 1em 1em;
text-align: center;
}

.player__track {
font-size: 1.15em;
margin: 1.25em 0 0.05em 0;
color: #55656c;
}

.player__album {
font-size: 12px;
margin: 0;
color: #bbc1c3;
}

.player__album-name,
.player__artist {
color: #adb5b8;
}

/* Keyframes Animation */
@-webkit-keyframes anim-ripple {
0% {
opacity: 0;
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
50% {
opacity: 1;
}
100% {
opacity: 0;
-webkit-transform: scale3d(3, 3, 1);
transform: scale3d(3, 3, 1);

}
}

@keyframes anim-ripple {
0% {
opacity: 0;
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
50% {
opacity: 1;
}
100% {
opacity: 0;
-webkit-transform: scale3d(3, 3, 1);
transform: scale3d(3, 3, 1);

}
}

Bagian berikutnya adalah file javascript, saya akan mulai menjelaskan semua proses animasi dari file ini sehingga mudah untuk memahami seluruh proses. Sekarang buatlah file javascript baru dan beri nama index.js. Buatlah kelas baru dan tambahkan konstruktor untuk menginisialisasikan semua variabel.

'use strict';
class MorphingButton{
constructor(){
//initilize all variables
this.btn = document.querySelector('.morpher a');
this.meta = document.querySelector('.player');
this.btnClose = document.querySelector('.button--close');

this.onClick = this.onClick.bind(this);
this.onCardClose = this.onCardClose.bind(this);
//add all even listener needed
this.addEventListeners();
}
}
window.addEventListener('load',() => new MorphingButton());

Sekarang tambahkan 3 fungsi baru onClick, onCardClose dan addEventListeners di kelas sebelumnya. addEventListeners digunakan untuk menambahkan semua listener yang akan kita gunakan, onClick akan digunakan untuk mendengarkan event klik pada tombol dan onCardClose akan digunakan untuk mendengarkan klik event pada tombol tanda silang di dalam kartu padainformasi lagu.

addEventListeners(){
this.btn.addEventListener('click',this.onClick);
this.btnClose.addEventListener('click',this.onCardClose);
}
onCardClose(e){
e.preventDefault();
this.hideCard();
}
onClick(e){
e.preventDefault();
let self = this;
// start listening state
this.listening();

//simulate listening for song in 5000 miliseconds
setTimeout(function(){
// stop listening state
self.stopListening();
// show card with song information
self.showCard();
// clear current Timeout
clearTimeout(this);
},5000);
}

Pada kode di atas dalam fungsi onClick kita perlu menyimpan innstance kelas pada variabel self karena setTimeout berjalan di thread berbeda, sehingga kita tidak mungkin dapat mengakses fungsi yang ada pada kelas saat ini didalam fungsi setTimeout.

Sekarang mari kita membuat bagian animasi dan membuat 4 fungsi baru yaitu listening, stopListening, Showcard dan hideCard. Pertama mari kita mulai dengan fungsi listening dimana fungsi ini kita menambahkan kelas listening pada elemen btn, jika Anda melihat kelas listening dengan pseudo class(kelas semu) ::after dan ::before di css, saya membuat border di mana border-radius diatur ke 50% sehingga akan membuat semacam cincin di tengah layar dan menganimasikannya dengan keyframes anim-ripple di mana animasi akan selalu berjalan karena saya mengatur animation-iteration-count menjadi infinit. Pada pseudo class ::after saya mengatur penundaan selama 0,6 detik sehingga ini akan membiarkan animasi di pseudo class ::before untuk berjalan terlebih dahulu selama 0,6 detik sebelum animasi di pseudo class ::after dieksekusi.

listening(){
// add class listening to circle-button
this.btn.className += ' listening';
}

Sekarang untuk fungsi stopListening kita hanya perlu menghapus kelas listening dari tombol untuk menghentikan animasi sehingga hanya kelas circle-button yang ada dalam elemen btn.

stopListening(){
// remove the listening class from button
this.btn.className = 'circle-button';
}

Berikutnya mari kita pindah ke fungsi showCard, dalam fungsi ini saya mengubah elemen induk dari btn menjadi kartu dengan menambahkan kelas card, ada beberapa urutan transisi untuk elemen ini dan urutannya berbeda saat kartu tampil dan saat kartu disembunyikan. Pada Showcard urutan transisi pertama adalah transform kemudian diikuti dengan border-radius. Jadi border-radius akan menunggu transform untuk menyelesaikannya terlebih dahulu sebelum dieksekusi. Dalam fungsi ini kita akan menyembunyikan btn dan mengatur opacity ke 0 serta menampilkan informasi lagu dengan menambahkan kelas player–visible pada elemen meta.

showCard(){
// add transition to morpher with 100ms delay on border-radius
this.btn.parentNode.style.transition = 'border-radius 100ms linear 100ms,transform 100ms linear';
//add transtion on opacity with duration 50ms
this.btn.style.transition = 'opacity 50ms linear';
//change button opacity to 0 and hide the button
this.btn.style.opacity = 0;
// add card class to morpher to transform it from circle button to card
this.btn.parentNode.className += ' card';
// add player--visible class to show track information
this.meta.className += ' player--visible';
}

Yang terakhir adalah fungsi hideCard. Dalam fungsi ini kita akan melakukan kebalikan dari fungsi Showcard dimana kita akan mengubah kembali elemen induk dari btn ke menjadi lingkaran yang memiliki bentuk yang sama dengan elemen btn. Urutan transisinya kebalikan dari fungsi Showcard, sehingga urutan transisinya adalah border-radius pertama dan kemudian diikuti dengan transform. Setelah itu kita akan menampilkan kembali elemen btn dan menyembunyikan informasi lagu.

hideCard(){
// remove player--visible to hide the song information
this.meta.className = 'player';
// add transition to morpher with 100ms delay on transform
this.btn.parentNode.style.transition = 'border-radius 100ms linear,transform 100ms linear 100ms';
// remove card class from morpher
this.btn.parentNode.className = 'morpher';
//add transtion on opacity with duration 200ms and delay 200ms
this.btn.style.transition = 'opacity 200ms linear 200ms';
//change button opacity to 1 and show the button
this.btn.style.opacity = 1;
}

Mungkin itu saja yang dapat saya share pada postingan kali ini, untuk sourcecode dapat di download melalui Github.

Saturday, November 12, 2016

November 12, 2016

Javascript Promises


Pada kesempatan kali ini saya akan sedikit membahas tentang Javascript Promises yang saya kutip dari situs Google Developers tentang web dasar yaitu Javascript Promises: an introduction.

Javascript adalah pemrograman single threaded, yang berarti bahwa 2 bit script tidak dapat dijalankan secara bersamaan, script tersebut harus dijalankan satu demi satu. Di browser javascript membagi thread dengan hal-hal lain yang berbeda dari browser ke browser. Tapi biasanya javascript berada diantrian yang sama seperti painting/pelukisan, memperbarui gaya (CSS) dan penanganan tindakan pengguna (seperti menyoroti teks dan berinteraksi dengan kontrol form). Aktivitas di salah satu hal-hal ini dapat menunda aktivitas lain.

Anda Mungkin telah menggunakan event dan callback untuk hal-hal seperti ini. Berikut adalah contoh event
var img1 = document.querySelector('.img-1');

img1.addEventListener('load', function() {
// gambar telah dimuat
});

img1.addEventListener('error', function() {
// gagal
});

Sayangnya pada contoh diatas, ada kemungkinan bahwa event terjadi sebelum kita mulai mendengarkan event tersebut, namun kita dapat mengatasinya menggunakan property "complete" yang ada pada element img.
var img1 = document.querySelector('.img-1');

function loaded() {
// gambar telah dimuat
}

if (img1.complete) {
loaded();
}
else {
img1.addEventListener('load', loaded);
}

img1.addEventListener('error', function() {
// gagal
});
Cara ini tidak menangkap gambar yang error sebelum kita mendapat kesempatan untuk mendengarkannya; sayangnya DOM tidak memberi kita cara untuk melakukan itu. Juga, ini hanya memuat satu gambar, hal ini akan menjadi lebih rumit jika kita ingin tahu kapan serangkaian gambar telah dimuat.



Event tidak selalu menjadi cara yang terbaik


Event sangat baik untuk hal-hal yang dapat terjadi beberapa kali seperti objek-keyup, touchstart dll Dengan event-event tersebut Anda tidak perlu peduli tentang apa yang terjadi sebelum Anda mulai mendengarkan event. Tapi ketika kita berbicara tentang async keberhasilan / kegagalan, idealnya Anda ingin sesuatu seperti ini:

img1.panggilIniJikaTelahDimuatAtauSaatDimuat(function() {
// telah dimuat
}).AtauJikaGagalPanggilIni(function() {
// gagal
});

// and…
ketikaSemuaIniTelahDimuat([img1, img2]).panggilIni(function() {
// semua telah dimuat
}).atauJikaSalahSatuAtauLebihGagalPanggilIni(function() {
// salah satu atau lebih gagal
});

Ini adalah apa yang Promises dapat lakukan, tetapi dengan penamaan yang lebih baik. Jika elemen gambar HTML memiliki metode "ready" yang mengembalikan Promise, kita bisa melakukan ini:
img1.ready().then(function() {
// telah dimuat
}, function() {
// gagal
});

// dan…
Promise.all([img1.ready(), img2.ready()]).then(function() {
// semua telah dimuat
}, function() {
// satu atau lebih gagal
});

Pada dasarnya, Promise mirip seperti event listener namun:
  1. Sebuah Promise hanya dapat berhasil atau gagal sekali. Hal ini tidak dapat berhasil atau gagal dua kali, tidak bisa juga beralih dari berhasil ke gagal atau sebaliknya.
  2. Jika Promise telah berhasil atau gagal dan nanti Anda menambahkan callback keberhasilan / kegagalan, callback yang benar akan dipanggil, meskipun event telah berlangsung sebelumnya.

Terminologi Promise

Berikut adalah dasar-dasarnya.
Promise dapat:
terpenuhi - Jika tindakan yang berkaitan dengan Promise berhasil
ditolak - Jika tindakan yang berkaitan dengan Promise gagal
tertunda - Apakah belum terpenuhi atau ditolak
menetap - Telah dipenuhi atau ditolak
spesifikasinya juga menggunakan istilah thenable untuk menggambarkan obyek yang seperti Promise, dalam hal ini Promise memiliki metode then.

Referensi Promise API

Semua metode bekerja di Chrome, Opera, Firefox, Microsoft Edge, dan Safari kecuali dinyatakan berbeda. polyfill menyediakan beberapa metode di bawah ini untuk semua brower.

Metode-metode Static
Ringkasan Metode
Promise.resolve(promise) Pengembalian Promise (hanya jika promise.constructor == Promise)
Promise.resolve(thenable); Membuat Promise baru dari thenable tersebut. Sebuah thenable adalah Promise yang memiliki metode `then()`.
Promise.resolve(obj); Membuat Promise yang terpenuhi untuk obj. dalam kasus ini.
Promise.reject(obj); Membuat Promise yang menolak untuk obj. Untuk konsistensi dan debugging (mis stack trace), obj harus menjadi instanceof Error.
Promise.all(array); Membuat Promise yang terpenuhi ketika setiap item dalam array terpenuhi, dan menolak jika (dan ketika) item menolak. Setiap item array akan diteruskan ke Promise.resolve, sehingga array dapat menjadi campuran objek Promise dan objek-objek lainnya. Nilai pemenuhan adalah array (dalam urutan) dari nilai pemenuhan. Nilai penolakan adalah nilai penolakan pertama.
Promise.race(array); Membuat Promise yang terpenuhi setelah salah satu item terpenuhi, atau menolak setelah slah satu item apapun menolak, mana yang terjadi pertama kali.

Thursday, November 10, 2016

November 10, 2016

Membuat Custom Element Di HTML Menggunakan Javascript



Pada kesempatan kali ini saya akan membahas tentang bagaimana cara Membuat Custom Element HTML Menggunakan javascript. Biasanya banyak developer yang masih kurang puas akan fitur-fitur dari element yang ada di HTML, sehingga kebanyakan dari mereka lebih senang untuk membuat custom elementnya sendiri. Pada postingan kali ini saya akan mencoba membuat conditional element dimanan nantinya element akan menampilkan child element pada kondisi tertentu.

Sebelum memulai buatlah file baru dan beri nama index.html kemudian copy template berikut.
<html>
<head>
<meta name="author" content="Muhammad Sayuti">
<title>Custom Elements</title>
</head>
<body>
<input type="checkbox" id="checkbox">
<dev-if if="checkbox is checked">
Checkbox is checked
</dev-if>
<script src="index.js"></script>
</body>
</html>

Sekarang buat file javascript baru dan beri nama index.js. Untuk membuat custom element kita akan menggunakan fungsi customElements untuk memberitahu browser tentang adanya tag baru yang kita buat. pada file ini buatlah class baru kemudian extends class tersebut dengan class DOM level 2 yaitu HTMLElement.
class DevIf extends HTMLElement{
constructor(){
// If you define a ctor, always call super() first!
// This is specific to CE and required by the spec.
super();
}
}

Jika Anda membuat suatu class dengan melakukan extends pada class yang ada pada DOM seperti HTMLElement, HTMLSelectElement dan sebagainya, sangat disarankan untuk memanggil super() pada bagian constructor() terlebih dahulu. Pada DOM level 2 kita dapat menggunakan beberapa callback sesuai keperluan yang akan kita gunakan pada custom element yang kita buat. Berikut adalah beberapa callback yang dapat Anda gunakan.

  • createdCallback() (Mozilla) : Biasanya callback ini akan dipanggil/dieksekusi saat element Anda teregister/terdaftar di DOM.
  • attachedCallback (Mozilla) atau connectedCallback() (Chrome) :
    Biasanya callback ini akan dipanggil /dieksekusi saat element Anda dimasukkan ke dalam DOM.
  • detachedCallback() (Mozilla) atau disconnectedCallback() (Chrome):
    Biasanya callback ini akan dipanggil /dieksekusi saat element Anda dihapus dari DOM.
  • attributeChangedCallback() (Mozilla & Chrome) :
    Biasanya callback ini akan dipanggil /dieksekusi saat ada perubahan pada attribute dari element yang Anda buat.
Di kasus ini saya menggunakan Chrome browser jadi kita akan fokus ke callback yang ada di Chrome browser.
Sekarang tambahkan 2 callback yaitu connectedCallback() dan attributeChangedCallback().
class DevIf extends HTMLElement{
constructor(){
// If you define a constructor, always call super() first!
// This is specific to CE and required by the specification.
super();
}
connectedCallback(){
//get value from if attribute and define new if variable on the current class
this.if = this.getAttribute('if');
}
attributeChangedCallback(name, oldValue, newValue){
if (name !== 'if') {
return;
}

this.if = newValue;
}
}

Pada script diatas saya memberikan value pada variable if dengan mengambil attribute if pada element dan mengubah value if jika terdapat perubahan pada attribute if di fungsi attributeChangedCallback(). Sekarang kita akan membuat getter and setter untuk variabel if, tambahkan script berikut kedalam class.
get if(){
return this.if;
}
set if(_condition){
if(!_condition){
return;
}

}
Selanjutnya kita akan membuat fungsi baru untuk melakukan parsing dan mengecek kondisi yang diberikan pada attribut if, buatlah fungsi baru checkCondition()
checkCondition(condition){
let parsed = condition.split(' ');
switch(parsed[1]){
case 'is':
//get element by id provided in first parameter
let el = document.querySelector(`#${parsed[0]}`);
//if element not found, warn the user and stop execution.
if(el == null){
console.warn('cannot find element with id',parsed[0]);
return;
}
// get the element type
let type = this.getElementType(el);
// check default state in our element for current condition
this.checkDefault(el);
// add listener to listen change on our element
this.addListener(el,type);
break;
}
}
Karena disini hanya demo jadi saya hanya akan memberikan 1 kondisi, jika Anda ingin mengembangkannya dan menambahkan kondisi lain Anda dapat mendapatkan source codenya melalui github. Pada script diatas saya melakukan split pada value yang diberikan di attribut if. variabel parsed akan menjadi array dan pada index 0 akan kita gunakan sebagai acuan untuk mencari element berdasarkan id, kemudian pada index 1 merupakan kondisinya, disini hanya ada kondisi "is", kemudian pada index 2 adalah kondisi apa yang akan digunakan. Pada contoh ini saya menggunakan element checkbox sebagai parameter pertama, is untuk parameter kedua dan checked pada parameter ketiga. Disini saya tidak melakukan pengecekan pada parameter ketiga karena jika kita menggunakan checkbox maka yang dicek sudah pasti apakah checkbox tersebut di check atau tidak.
Sekarang buat fungsi baru untuk mendapatkan tipe element yang diberikan pada parameter pertama jika element ditemukan.
getElementType(target){
if(target.nodeName === 'input'){
return target.getAttribute('type');
}else{
return target.nodeName.toLowerCase();
}
}
Setelah itu buat fungsi baru untuk menentukan apa yang terjadi pada element berdasarkan kondisi saat ini.
checkDefault(target,type){
if(type === 'checkbox'){
if(target.checked || target.hasAttribute('checked')){
this.style.display = 'initial';
}else{
this.style.display = 'none';
}
}
}
Sekarang buat fungsi baru lagi untuk menambahkan listener saat ada perubahan yang terjadi pada element dan memanggil fungsi checkDefault().
addListener(target,type){
let self = this;
target.addEventListener('change',function(){
let t = self.getElementType(this);
self.checkDefault(this,t);
});
}
Untuk langkah terakhir kita hanya perlu mendefinisikan element yang akan kita buat dengan menggunakan fungsi customElements() yang ada di window. parameter pertama merupakan nama tag yang kita berikan sedangkan yang kedua adalah class yang baru saja kita buat. Letakkan script berikut di baris paling akhir dalam file javascript Anda.
cusomElements.define('dev-if',DevIf);

Sekarang cek hasilnya pada Chrome browser Anda, element yang ada di dalam custom element yang kita buat hanya akan tampil saat checkbox di check. berikut hasil akhirnya.
See the Pen conditional-element by Muhammad Sayuti (@ekoputrapratama) on CodePen.


Mungkin itu saja yang dapat saya jelaskan pada postingan kali ini tentang bagaimana cara Membuat Custom Element HTML Menggunakan Javascript.
Semoga bermanfaat...!!

Saturday, October 22, 2016

October 22, 2016

Membuat SlidingUpPanel Dengan HTML,CSS Dan Javascript Bagian 4





Setelah sebelumnya di postingan Membuat SlidingUpPanel Dengan HTML, CSS Dan Javascript Bagian 3 kita telah membuat pemutar musiknya, maka pada postingan terahir di dalam topik ini kita akan memperbaiki beberapa element serta sedikit memberi efek untuk mempercantik tampilan antarmuka.

Pertama-tama kita akan mengedit pada bagian panel header dimana content dari panel header akan kita bagi menjadi 2, yaitu panel header saat posisi SlidingUpPanel terbuka dan panel header saat posisi SlidingUpPanel tertutup atau berada dibagian dibawah layar.
Ubah bagian sliding-up-panel-header menjadi seperti berikut




SlidingUpPanel Music Player























Sekarang tambahkan css berikut kedalam file sliding-up-panel.css.
#sliding-up-panel-header.expanded .panel-header-content__expanded{
transition:opacity 300ms;
opacity:1;
}
#sliding-up-panel-header .panel-header-content__expanded{
position: absolute;
transition: opacity 300ms;
opacity:0;
text-align: left;
padding:20px;
pointer-events: none;
}
#sliding-up-panel-header .panel-header-content__collapsed{
pointer-events: none;
}
#sliding-up-panel-header.expanded .panel-header-content__collapsed{
display: none;
}

Sekarang kita akan memperbaiki beberapa bug yang mungkin telah Anda lihat saat kita mendrag panel header lebih dari tinggi layar maka panel header akan overflow dan tidak nampak dilayar, untuk mengatasinya kita perlu menambahkan statement if saat user mendrag header dengan mengecek apakah posisi sumbu y saat ini kurang dari ukuran saat SlidingUpPanel pada posisi terbuka dan apakah posisi sumbu y saat ini lebih dari 0. Tambahkan 2 baris statement if berikut pada fungsi update di file sliding-up-panel.js tepat sebelum this.slidingPanel.style.transform = `translateY(${to}px)`;

//prevent the header to overflow the screen height when dragging
if(to < this.expandedY) to = this.expandedY;
if(to > 0) to = 0;

Selanjutnya masih di file yang sama, ubah fungsi animateSlidingPanelIntoPosition menjadi seperti berikut.
animateSlidePanelIntoPosition (target) {
if(!target){
return;
}
// create new function variable to excecute when the transition has ended
const onAnimationComplete = evt => {
const panel = evt.target;
panel.removeEventListener('transitionend', onAnimationComplete);
panel.style.transition = '';
};
// create and set panel to current slidingPanel
const panel = target;
//check if current state is expanded
if(this.isExpanded){
//add event listener when the transition has ended.
panel.addEventListener('transitionend', onAnimationComplete);
//set the panel transition to transform with duration 200 miliseconds
panel.style.transition = `transform 200ms`;
//set the panel transform to position when it's in expanded state
panel.style.transform = `translateY(${this.expandedY}px)`;
//add class expanded to panel header
this.panelHeader.setAttribute('class','expanded');
}else{
//add event listener when the transition has ended.
panel.addEventListener('transitionend', onAnimationComplete);
//set the panel transition to transform with duration 200 miliseconds
panel.style.transition = `transform 200ms`;
//set the panel transform to position when it's not in expanded state
panel.style.transform = `translateY(0px)`;
//remove class attribute from panel header
this.panelHeader.removeAttribute('class')
}
// user has finish dragging the header, set target to null
if(!this.draggingHeader)
this.target = null;
}

Selanjutnya kita akan menampilkan preloader saat playlist masih kosong dan menyembunyikannya saat playlist telah terisi. Disini kita juga akan memberikan sedikit efek fade in dan slide.
Tambahkan tag div dengan nama class preloader tepat setelah tag audio.


Sekarang ubah css .playlist ul li serta tambahkan .playlist.loading ul li dan .preloader dalam file player.css menjadi seperti berikut.
.preloader{
position: absolute;
width:46px;
height:46px;
top:calc(50% - 23px);
left:calc(50% - 23px);
background-image: url(../assets/img/ajax-loader.gif);
}
.playlist ul li{
display: block;
border-bottom: 1px solid black;
transform: translate3d(0,0,0);
opacity:1;
}
.playlist.loading ul li{
transform: translate3d(100%,0,0);
opacity:0;
}
Selanjutnya buat variabel baru pada fungsi construct untuk menyimpan element preloader dan ubah fungsi loadPlaylistMetadata dan getPlaylist pada file player.js menjadi seperti berikut.
this.preloader = document.querySelector('.preloader');
getPlaylist(){
var self = this;
var xhr = new XMLHttpRequest();
xhr.onload = function(e){
e.preventDefault();
if(xhr.status == 200 || xhr.status == 409){
var data = JSON.parse(xhr.response.match(/\[.*\]/ig, "")[0]);
data.forEach(function(item,index){
self.loadPlaylistMetadata(item,index,data.length);
});
self.songs = Array.from(document.querySelectorAll('.song a'))
}else{
console.log('cant get playlist, error with status code ' + xhr.status);
}
}
xhr.open('get', '\songlist', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send();
}
loadPlaylistMetadata(path,index,length){
var self = this;
// create new li element for the playlist item and set the attribute
var li = document.createElement('li');
li.setAttribute('class','song');
li.setAttribute('data-index',index);

// create new link element and set the href attribute to the file path
var a = document.createElement('a');
a.setAttribute('href',path);

// create new image element
var img = document.createElement('img');
img.className = 'cover';
img.style.cssFloat = 'left';

//create new container for title and artist metadata
var meta = document.createElement('div');
meta.className = 'metatag playlist';

//create new span element to set title from the file metadata
var title = document.createElement('span');
title.className = 'title';
title.style.whiteSpace = 'nowrap';

//create new span element to set artist name from the file metadata
var artist = document.createElement('span');
artist.className = 'artist';
//insert link/anchor element to li element
li.appendChild(a);
//insert img element to link/anchor element
a.appendChild(img);
//insert meta container to the link/anchor element
a.appendChild(meta);
/* create new p element and insert title and artist to it then append it to meta container,
* this element is necessary if we want the title or artist to
* be marqueed if the width is more than container width.*/
var p = document.createElement('p').appendChild(title);
meta.appendChild(p);
p = document.createElement('p').appendChild(artist);
meta.appendChild(p);
// listen to link click event
a.onclick = function(e){
e.preventDefault();
self.player.setAttribute('src',path);
self.songChanged(self.currentSong,li.getAttribute('data-index'));
self.currentSong = parseInt(li.getAttribute('data-index'));
self.playPauseBtn[0].children[0].click();
};
//append li element to playlist
this.playlist.children[0].appendChild(li);
// read meta data from the file using jsmediatags
jsmediatags.read(path, {
onSuccess: function(tag) {
var url = path;
var filename = url.split('/');
filename = filename[filename.length-1];
if(tag.tags.picture != undefined){
var base64String = "";
for (var i = 0; i < tag.tags.picture.data.length; i++) {
base64String += String.fromCharCode(tag.tags.picture.data[i]);
}
img.setAttribute('src',"data:"+ tag.tags.picture.format +";base64," + window.btoa(base64String));
}else{
img.setAttribute('src',"assets/img/albumart_mp_unknown.png");
}
// if the artist name in metadata is undefined or null, set it to unknown.
artist.innerHTML = tag.tags.artist || 'unknown';
// if the title in metadata is undefined or null, use filename instead.
title.innerHTML = tag.tags.title || filename;

// setup marquee animation
self.setupMarqueeAnimation(titles[i],artists[i]);

if(index == length - 1){
//hide the preloader
self.preloader.style.display = 'none';
//get all li element in playlist
let list = self.playlist.querySelectorAll('li');
list.forEach(function(item,index){
//add transition with diffrent delay for each item in list depend on item index/position in the list
item.style.transition = `transform 300ms ease-in ${index*0.2}s, opacity 300ms ease-in ${index*0.2}s`;
});
//change playlist class name, this will remove loading class
self.playlist.setAttribute('class','playlist');
}
},
onError: function(error) {
console.log(error);
}
});
}
Terakhir tambahkan class loading pada playlist di dalam file index.html menjadi seperti berikut.



    Untuk file preloader Anda dapat menggunakan gambar berikut dan menyimpannya di folder assets > img.



    Mungkin sampai disini saja pada postingan terakhir dalam topik Membuat SlidingUpPanel Dengan HTML,CSS Dan Javscript ini, jika terdapat pertanyaan dapat menggunakan kotak komentar dibawah.

    Tuesday, October 11, 2016

    October 11, 2016

    Membuat SlidingUpPanel Dengan HTML,CSS Dan Javascript Bagian 3


    Pada postingan sebelumnya tentang Membuat SlidingUpPanel Dengan HTML, CSS Dan Javascript Bagian 2 kita telah selesai SlidingUpPanel, di postingan kali ini kita akan membuat javascript untuk memainkan lagu dari yang disimpan dalam folder songs.

    Buatlah file javascript baru di dalam folder proyek Anda beri nama player.js. Seperti sebelumnya ssaat membuat sliding-up-panel.js buatlah class MusicPlayer pada file tersebut kemudian tambahkan constructor pada kelas MusicPlayer dan inisialisasikan variable yang akan digunakan.
    'use strict';
    class MusicPlayer{
    constructor(){
    this.player = document.querySelector('audio');
    this.playlist = document.querySelector('.playlist');
    this.playPauseBtn = Array.from(document.querySelectorAll('.play-btn'));
    this.nextBtn = document.querySelector('.next-btn');
    this.prevBtn = document.querySelector('.prev-btn');

    this.songs = Array.from(document.querySelectorAll('.song a'));
    this.currentSong = 0;

    this.onPlayPause = this.onPlayPause.bind(this);
    this.onNextClick = this.onNextClick.bind(this);
    this.onPreviousClick = this.onPreviousClick.bind(this);
    this.onProgress = this.onProgress.bind(this);
    this.onMetadataLoaded = this.onMetadataLoaded.bind(this);
    this.onSongEnded = this.onSongEnded.bind(this);
    this.songChanged = this.songChanged;
    this.setupMarqueeAnimation = this.setupMarqueeAnimation.bind(this);
    this.addEventListeners();
    this.initPlaylist();

    }
    }
    window.addEventListener('load',() => new MusicPlayer());

    Sekarang buat fungsi untuk menginisialisasikan daftar putar dengan melakukan XMLHttpRequest ke sisi server dan menerima semua nama file yang ada dalam folder songs.
    initPlaylist(){
    this.getPlaylist();
    }
    getPlaylist(){
    var self = this;
    return new Promise(function(resolve,reject){
    var xhr = new XMLHttpRequest();
    xhr.onload = function(e){
    e.preventDefault();
    if(xhr.status == 200 || xhr.status == 409){
    //parse the response to JSON Array
    var data = JSON.parse(xhr.response.match(/\[.*\]/ig, "")[0]);
    // loopping through all item in array, where each item contain link to the file.
    data.forEach(function(item,index){
    // load metadata from the file
    self.loadPlaylistMetadata(item,index);
    });
    resolve(data);
    }else{
    reject('cant get paylist');
    }
    }
    // open XMLHttpRequest with GET method
    xhr.open('get', '\songlist', true);
    // set header Content-Type to JSON
    xhr.setRequestHeader('Content-Type', 'application/json');
    // send the request to server
    xhr.send();
    });
    }
    loadPlaylistMetadata(path,index){
    self =this;
    // create new li element for the playlist item and set the attribute
    var li = document.createElement('li');
    li.setAttribute('class','song');
    li.setAttribute('data-index',index);

    // create new link element and set the href attribute to the file path
    var a = document.createElement('a');
    a.setAttribute('href',path);

    // create new image element
    var img = document.createElement('img');
    img.className = 'cover';
    img.style.cssFloat = 'left';

    //create new container for title and artist metadata
    var meta = document.createElement('div');
    meta.className = 'metatag playlist';

    //create new span element to set title from the file metadata
    var title = document.createElement('span');
    title.className = 'title';
    title.style.whiteSpace = 'nowrap';

    //create new span element to set artist name from the file metadata
    var artist = document.createElement('span');
    artist.className = 'artist';
    //insert link/anchor element to li element
    li.appendChild(a);
    //insert img element to link/anchor element
    a.appendChild(img);
    //insert meta container to the link/anchor element
    a.appendChild(meta);
    /* create new p element and insert title and artist to it then append it to meta container,
    * this element is necessary if we want the title or artist to
    * be marqueed if the width is more than container width.*/
    var p = document.createElement('p').appendChild(title);
    meta.appendChild(p);
    p = document.createElement('p').appendChild(artist);
    meta.appendChild(p);
    // listen to link click event
    a.onclick = function(e){
    e.preventDefault();
    self.player.setAttribute('src',link);
    self.songChanged(self.currentSong,li.getAttribute('data-index'));
    self.currentSong = parseInt(li.getAttribute('data-index'));
    self.playPauseBtn[0].children[0].click();
    };
    //append li element to playlist
    this.playlist.children[0].appendChild(li);
    // read meta data from the file using jsmediatags
    jsmediatags.read(path, {
    onSuccess: function(tag) {
    var url = path;
    var filename = url.split('/');
    filename = filename[filename.length-1];
    if(tag.tags.picture != undefined){
    var base64String = "";
    for (var i = 0; i < tag.tags.picture.data.length; i++) {
    base64String += String.fromCharCode(tag.tags.picture.data[i]);
    }
    img.setAttribute('src',"data:"+ tag.tags.picture.format +";base64," + window.btoa(base64String));
    }else{
    img.setAttribute('src',"assets/img/albumart_mp_unknown.png");
    }
    // if the artist name in metadata is undefined or null, set it to unknown.
    artist.innerHTML = tag.tags.artist || 'unknown';
    // if the title in metadata is undefined or null, use filename instead.
    title.innerHTML = tag.tags.title || filename;

    // setup marquee animation
    self.setupMarqueeAnimation(titles[i],artists[i]);

    },
    onError: function(error) {
    console.log(error);
    }
    });
    }
    Buatlah fungsi baru untuk menambahkan animasi marquee pada title/artist jik lebarnya melebihi container/parent.
    setupMarqueeAnimation(title,artist){
    if(title.offsetWidth > parseInt(title.parentNode.style.width.replace('px',''))){
    title.className += ' marquee-title';
    }else{
    title.style.willChange = 'initial';
    title.className = 'title';
    }
    if(artist.offsetWidth > parseInt(artist.parentNode.style.width.replace('px',''))){
    artist.className += ' marquee-artist';
    }else{
    artist.className = 'artist';
    }
    }
    Setelah itu buat fungsi untuk menambahkan event listener yang dibutuhkan.
    addEventListeners(){
    this.player.ontimeupdate = this.onProgress;
    this.player.onended = this.onSongEnded;
    this.player.onloadedmetadata = this.onMetadataLoaded;
    for(var i in this.playPauseBtn){
    this.playPauseBtn[i].addEventListener('click',this.onPlayPause);
    }
    this.nextBtn.addEventListener('click',this.onNextClick);
    this.prevBtn.addEventListener('click',this.onPreviousClick);
    }
    Sekarang buat fungsi-fungsi yang digunakan di event listener diatas.
    onSongEnded(e){
    this.moveToNext();
    }
    onNextClick(e){
    this.moveToNext();
    e.preventDefault();
    }
    onPreviousClick(e){
    this.moveToPrevious();
    e.preventDefault();
    }
    onPlayPause(e){
    if(this.player.getAttribute('src') == ''){
    this.player.setAttribute('src',this.songs[0].getAttribute('href'));
    this.currentSong = 0;
    this.songChanged(null,0);
    }
    if(this.player.paused){
    this.player.play().then().catch(function(error){
    console.log(error);
    });
    for(var i in this.playPauseBtn){
    this.playPauseBtn[i].children[0].setAttribute('src','assets/icons/pause-btn.svg');
    this.playPauseBtn[i].setAttribute('state','play');
    }
    }else{
    try{
    this.player.pause();
    for(var i in this.playPauseBtn){
    this.playPauseBtn[i].children[0].setAttribute('src','assets/icons/play-btn.svg');
    this.playPauseBtn[i].setAttribute('state','play');
    }
    }catch(error){
    console.log(error);
    }
    }
    e.preventDefault();
    }
    onMetadataLoaded(e){
    var self = this;
    var artists = document.querySelectorAll('.meta .artist');
    var titles = document.querySelectorAll('.meta .title');
    var covers = Array.from(document.querySelectorAll('img.coverart'));
    jsmediatags.read(e.target.src, {
    onSuccess: function(tag) {
    var url = e.target.src;
    var filename = url.split('/');
    filename = filename[filename.length-1]
    if(tag.tags.picture != undefined){
    for(var j in covers){
    var base64String = "";
    for (var i = 0; i < tag.tags.picture.data.length; i++) {
    base64String += String.fromCharCode(tag.tags.picture.data[i]);
    }
    covers[j].setAttribute('src',"data:"+ tag.tags.picture.format +";base64," + window.btoa(base64String));
    }
    }else{
    for(var j in covers){
    covers[j].setAttribute('src',"assets/img/albumart_mp_unknown.png");
    }
    }
    for(var i = 0; i < artists.length;i++){
    titles[i].parentNode.style.width = 200;
    artists[i].parentNode.style.width = 200;
    artists[i].innerHTML = tag.tags.artist;
    titles[i].innerHTML = tag.tags.title;

    // if the artist name in metadata is undefined or null, set it to unknown.
    artist.innerHTML = tag.tags.artist || 'unknown';
    // if the title in metadata is undefined or null, use filename instead.
    title.innerHTML = tag.tags.title || filename;

    // setup marquee animation
    self.setupMarqueeAnimation(titles[i],artists[i]);
    }

    },
    onError: function(error) {
    console.log(error);
    }
    });
    }

    moveToNext(){
    var next = this.currentSong + 1,link;
    if(next < this.songs.length){
    link = this.songs[next].getAttribute('href');
    this.songChanged(this.currentSong,next);
    this.currentSong = next;
    this.player.setAttribute('src',link);

    }else{
    link = this.songs[0].getAttribute('href');
    this.songChanged(this.currentSong,0);
    this.currentSong = 0;
    this.player.setAttribute('src',link);
    }
    this.player.load();
    this.player.play();
    }
    moveToPrevious(){
    var prev = this.currentSong - 1,link;
    if(prev >= 0){
    link = this.songs[prev].getAttribute('href');
    this.songChanged(this.currentSong,prev);
    this.currentSong = prev;
    this.player.setAttribute('src',link);

    }else{
    prev = this.songs.length - 1;
    var link = this.songs[prev].getAttribute('href');
    this.songChanged(this.currentSong,prev);
    this.currentSong = prev;
    this.player.setAttribute('src',link);
    }
    this.player.play();
    }
    Kemudian buat fungsi baru lagi untuk mengubah warna background dari item yang saat ini sedang dimainkan.
    songChanged(before,current){
    //if the song before the current one is not null, remove active class from that element
    if(before != null){
    this.songs[before].removeAttribute('class');
    }
    // set the current song item class in playlist to active
    this.songs[current].setAttribute('class','active');
    }
    Sampai disini pembuatan pemutar musiknya telah selesai, sekarang buka cmd/terminal kemudiang ketikkan perintah supervisor index.js kemudian buka browser dan ubah url menjadi http:/localhost:3000 untuk melihat hasilnya. Kurang lebih hasilnya menjadi akan seperti berikut.



    untuk postingan selanjutnya kita akan memperbaiki beberapa tampilan termasuk panel header yang akan kita buat berganti tampilan saat kita membuka panel. :)

    Monday, October 10, 2016

    October 10, 2016

    Membuka Semua Tautan Eksternal di Tab Baru

    Untuk mencegah pembaca blog Anda meninggalkan blog tanpa membaca satu atau hanya satu pos, Anda harus berpikir untuk membuka tautan dari blog Anda di tab baru.

    Membuka Semua Tautan Eksternal di Tab Baru

    Ini juga membantu meningkatkan keseluruhan tampilan halaman dan mengurangi tingkat bouncing. Tingkat bouncing Blog adalah salah satu faktor penting dalam blogging dan hanya didefinisikan sebagai jumlah pembaca yang meninggalkan blog Anda tanpa membaca satu atau hanya satu pos. Ini berarti lebih banyak tingkat bouncing.
    Selengkapnya »

    Friday, October 7, 2016

    October 07, 2016

    Membuat SlidingUpPanel Dengan HTML,CSS Dan Javascript Bagian 2


    Pada postingan sebelumnya tentang Membuat SlidingUpPanel Dengan HTML, CSS Dan Javascript Bagian 1 kita telah selesai membuat desain awal aplikasi, di postingan kali ini kita akan bermain dengan javascript untuk membuat SlidingUpPanel yang kita buat sebelumnya dapat bekerja sesuai apa yang kita inginkan.

    Pertama buatlah file javascript baru beri nama sliding-up-panel.js kemudian tuliskan "use strict";. Mode use strict ini berguna untuk menempatkan program atau fungsi dalam operasi konteks yang strict (ketat). konteks yang ketat ini mencegah tindakan-tindakan tertentu dan melempar lebih banyak exception/pengecualian.

    Disini kita akan membuat kelas baru dengan nama SlidingUpPanel dan memuat kelas pada event window onload.
    "use strict";
    class SlidingUpPanel{
    constructor(){

    }
    }
    window.addEventListener('load',() => new SlidingUpPanel());

    Untuk mengetes apakah kelas yang kita buat bekerja kita akan menulis ke console, menjadi seperti berikut.
    "use strict";
    class SlidingUpPanel{
    constructor(){
    console.log('Hello World!')
    }
    }
    window.addEventListener('load',() => new SlidingUpPanel());


    Sekarang buka cmd/terminal di dalam folder proyek Anda kemudian ketikan perintah berikut dan buka browser ketikkan url http://locahost:3000.
    supervisor index.js
    Kemudian tekan CTRL + SHIFT + I untuk membuka panel alat pengembang dan melihat hasilnya di bagian console.


    Jika Anda melihat tulisan "Hello World!" di console seperti yang terlihat pada gambar diatas, artinya kelas SlidingUpPanel yang kita buat bekerja. Sekarang kita perlu menginisialisasikan variabel yang dibutuhkan didalam fungsi constructor() dan mengganti console.log yang kita buat sebelumnya.
    class SlidingUpPanel{
    constructor(){
    this.slidingPanel = document.querySelector('#sliding-up-panel');
    this.panelHeader = document.querySelector('#sliding-up-panel-header');
    this.panelBody = document.querySelector('#sliding-up-panel-body');
    this.mainContent = document.body.children[0];
    this.mainContent.style.marginBottom = `${this.panelHeader.offsetHeight}px`;
    this.slidingPanel.style.height = window.innerHeight;
    this.slidingPanel.style.top = window.innerHeight - this.panelHeader.offsetHeight;

    this.target = null;
    this.targetBCR = null;
    this.panelBCR = this.slidingPanel.getBoundingClientRect();

    this.startY = 0;
    this.currentY = 0;
    this.screenY = 0;
    this.targetY = 0;
    this.collapsedY = this.panelBCR.height - this.panelHeader.offsetHeight;
    this.expandedY = -this.panelBCR.height + this.panelHeader.offsetHeight;

    this.draggingHeader = false;
    this.isExpanded = false;
    }
    }

    Sekarang kita akan membuat beberapa fungsi baru untuk mendengarkan event yang kita butuhkan diantaranya adalah :
    • Event mousedown
    • Event mousemove
    • Event mouseup
    • Event touchstart
    • Event touchmove
    • Event touchend
    • Event Window Resize
    Event touch digunakan agar nantinya support untuk perangkat layar sentuh. Namun pada dasarnya isi dari fungsi touch dan mouse sama, sedangkan event Window resize digunakan untuk mengubah posisi top pada SlidingUpPanel saat jendela browser berubah ukuran.
    Sekarang buatlah fungsi baru seperti berikut di dalam class SlidingUpPanel.
    addEventListeners(){
    document.addEventListener('touchstart', this.onStart);
    document.addEventListener('touchmove', this.onMove);
    document.addEventListener('touchend', this.onEnd);

    const self = this;
    window.addEventListener('resize',function(e){
    self.slidingPanel.style.top = window.innerHeight - self.panelHeader.offsetHeight;
    self.expandedY = -window.innerHeight + self.panelHeader.offsetHeight;
    self.animateSlidePanelIntoPosition(self.slidingPanel);
    e.preventDefault();
    });

    document.addEventListener('mousedown', this.onStart);
    document.addEventListener('mousemove', this.onMove);
    document.addEventListener('mouseup', this.onEnd);
    }
    Sekarang kita buat 3 fungsi yang akan digunakaan yaitu onStart, onMove dan onEnd.
    onStart (evt) {
    if (this.target)
    return;

    //if current target id is not sliding-up-panel-header, don't execute the next code.
    if (evt.target.id != 'sliding-up-panel-header')
    return;
    //set target to current touched/clicked target
    this.target = evt.target;
    //set targetBCR to current touched/clicked target Bounding Client Rectangle
    this.targetBCR = this.target.getBoundingClientRect();
    //set startY to current touched/clicked Y position on page
    this.startY = evt.pageY || evt.touches[0].pageY;
    //set currentY equal to startY
    this.currentY = this.startY;

    //set draggingHeader to true
    this.draggingHeader = true;
    /* because we will change the panel position based on Y axis using translateY
    * set property will-change to transform */
    this.slidingPanel.style.willChange = 'transform';

    evt.preventDefault();
    }
    onMove(evt){
    if (!this.target)
    return;
    //set currentY to current touched/clicked Y position on page when we drag the header
    this.currentY = evt.pageY || evt.touches[0].pageY;
    }
    onEnd(evt){
    if (!this.target)
    return;

    this.targetY = 0;
    //create new variable screenY and set current header panel position to it.
    let screenY = this.currentY - this.startY;
    /* threshold variable is used to check if user has drag more then 1/4 of panel height size
    * you can change this to half or whatever you want the slider to be expanded or not
    * when user drag more then threshold */
    const threshold = this.panelBCR.height * 0.25;
    if (Math.abs(screenY) > threshold) {
    /* set targetY to panel bounding client rectangle height subtract by panel header height if screenY is
    * positive value and set to negative value of panel bounding client rectangle height add by
    * panel header height */
    this.targetY = (screenY > 0) ?
    (this.panelBCR.height - this.panelHeader.offsetHeight) :
    (-this.panelBCR.height + this.panelHeader.offsetHeight);
    }
    //set draggingHeader to false
    this.draggingHeader = false;
    }
    Setelah semua fungsi telah dibuat sekarang kita akan melakukan bind pada fungsi yang kita buat tadi ke variabel dalam konteks kelas SlidingUpPanel. Tambahkan script berikut ke dalam fungsi constructor.
    this.onStart = this.onStart.bind(this);
    this.onMove = this.onMove.bind(this);
    this.onEnd = this.onEnd.bind(this);
    this.addEventListeners();
    Sekarang kita akan masuk ke bagian animasi dimana kita menggerakan sliding panel berdasarkan sumbu y saat kita melakukan drag pada panel header.
    Untuk menganimasikan panel disini saya menggunakan requestAnimationFrame agar browser dapat megoptimasikan animasi sehingga animasi menjadi lebih halus saat objek bergerak. buatlah fungsi baru seperti berikut.
    update () {
    requestAnimationFrame(this.update);

    if (!this.target)
    return;

    if (this.draggingHeader) {
    this.screenY = this.currentY - this.startY;
    } else {
    this.screenY += (this.targetY - this.screenY) / 4;
    }
    /* create new variable to and set the value to where we want the sliding panel to move based on Y axis.
    * if current panel state is expanded then we move it to expandedY add by screenY
    * if not then use screenY instead */
    let to = (this.isExpanded) ? (this.expandedY + this.screenY) : this.screenY;
    this.slidingPanel.style.transform = `translateY(${to}px)`;

    // Check if user still dragging the header or not, if true don't execute the next code.
    if (this.draggingHeader)
    return;
    const panel = this.target.parentNode;
    const isNearlyAtStart = (!this.isExpanded)?(Math.abs(this.screenY) < Math.abs(panel.offsetHeight/4)):(Math.abs(this.screenY) > Math.abs(panel.offsetHeight/4));
    const isNearlyExpanded = (!this.isExpanded)?(Math.abs(this.screenY) > Math.abs(panel.offsetHeight/4)):(Math.abs(this.screenY) < Math.abs(panel.offsetHeight/4));
    // If the panel is nearly expanded and current state is not expanded.
    if (isNearlyExpanded && !this.isExpanded) {
    // Bail if there's no target or it's not attached to a parent anymore.
    if (!this.target || !this.target.parentNode)
    return;
    // set isExpanded to true
    this.isExpanded = true;
    } else if (isNearlyAtStart) {
    // set isExpanded to false
    this.isExpanded = false;
    }
    // animate the panel to the position where we want it based on its state expanded or not.
    this.animateSlidePanelIntoPosition(this.target.parentNode);
    }
    Selanjutnya tambahkan script berikut ke dalam fungsi construct.
    this.update = this.update.bind(this);
    requestAnimationFrame(this.update);
    Sekarang kita akan membuat fungsi baru untuk menggerakkan panel pada posisinya berdasarkan keadaan panel yang disimpan dalam variabel isExpanded.
    animateSlidePanelIntoPosition (target) {
    if(!target){
    return;
    }
    // create new function variable to excecute when the transition has ended
    const onAnimationComplete = evt => {
    const panel = evt.target;
    //remove current listener from the panel
    panel.removeEventListener('transitionend', onAnimationComplete);
    panel.style.transition = '';

    };
    // create and set panel to current slidingPanel
    const panel = this.slidingPanel;
    //check if current state is expanded
    if(this.isExpanded){
    //add event listener when the transition has ended.
    panel.addEventListener('transitionend', onAnimationComplete);
    //set the panel transition to transform with duration 200 miliseconds
    panel.style.transition = `transform 200ms`;
    //set the panel transform to position when it's in expanded state
    panel.style.transform = `translateY(${this.expandedY}px)`;
    }else{
    //add event listener when the transition has ended.
    panel.addEventListener('transitionend', onAnimationComplete);
    //set the panel transition to transform with duration 200 miliseconds
    panel.style.transition = `transform 200ms`;
    //set the panel transform to position when it's not in expanded state
    panel.style.transform = `translateY(0px)`;
    }
    // animation has ended, set target to null
    this.target = null;
    }
    Sekarang buka kembali browser Anda kemudian lihat hasilnya di http://localhost:3000, kurang lebih hasilnya akan seperti berikut. Sampai disini Anda telah berhasil membuat SlidingUpPanel Layout, untuk posting selanjutnya kita akan mulai membuat javascript untuk pemutar musiknya. So stay Tuned ;)!! Untuk gambar-gambar yang digunakan dalam tutorial ini dapat di download disini.