Minggu, 31 Maret 2013

Plugin JavaScript Kotak Dialog

Screenshoot of JavaScript dialog box plugin.

Plugin kotak dialog ini pada dasarnya hanyalah pengembangan sederhana dari konsep draggable element yang pernah Saya tuliskan sebelumnya. Kotak dialog ini hanya menggunakan JavaScript mentah, jadi tentu saja akan memiliki beberapa keterbatasan, diantaranya adalah:

  1. Tidak ada fitur resizable.
  2. Tidak ada efek animasi.
  3. Tidak bisa membuka beberapa kotak dialog sekaligus, hanya satu kotak dialog dengan isi dan ukuran yang berubah-ubah.

Untuk lebih jelasnya bisa dilihat di halaman demo:

Memasang Plugin

Untuk memasang plugin kotak dialog, Anda perlu menambahkan file CSS ini di atas kode </head>:

<link rel="stylesheet" type="text/css" href="http://reader-download.googlecode.com/svn/trunk/dialog-box/dialog-box.min.css"/>

Kemudian tambahkan juga file JavaScript ini di atas </body>:

<script type="text/javascript" src="http://reader-download.googlecode.com/svn/trunk/dialog-box/dialog-box.min.js"></script>

Aktivasi

Konsep dasar aktivasi ada dua, yaitu membuka kotak dialog dan menutup kotak dialog. Untuk membuka kotak dialog berdasarkan aksi klik pada tombol, jalankan fungsi setDialog dengan parameter open. Sedangkan untuk menutup kotak dialog, gunakan parameter close:

<a href='javascript:setDialog("open");'>Buka kotak dialog</a>
<a href='javascript:setDialog("close");'>Tutup kotak dialog</a>

Daftar Konfigurasi Lengkap

Setelah Anda mengerti mengenai bagaimana caranya menampilkan dan menyembunyikan kotak dialog, selanjutnya Anda perlu mengetahui cara menyisipkan konten dan juga merekayasa tombol-tombol kotak dialog. Yang mana semua pengaturan itu akan dilakukan pada parameter ke dua, berupa objek seperti ini:

<a href='javascript:setDialog("open", { ... });'>Buka kotak dialog</a>
OpsiKeterangan
titleDigunakan untuk menentukan judul kotak dialog.
contentDigunakan untuk menentukan konten kotak dialog.
widthDigunakan untuk menentukan lebar kotak dialog dalam piksel.
heightDigunakan untuk menentukan tinggi kotak dialog dalam piksel.
topDigunakan untuk menentukan jarak atas kotak dialog terhadap layar.
leftDigunakan untuk menentukan jarak kiri kotak dialog terhadap layar.
specialClassDigunakan untuk menambahkan kelas CSS khusus pada kotak dialog (untuk keperluan modifikasi penampilan).
fixedJika bernilai true, posisi kotak dialog akan melayang dan tidak akan terbawa gulungan layar. Jika bernilai false, posisi kotak dialog bisa terbawa gulungan layar.
overlayPilihan untuk menampilkan atau menyembunyikan tabir kotak dialog.
buttonsAdalah deret objek yang nantinya akan berubah menjadi daftar tombol baru (akan Saya jelaskan nanti).

Konfigurasi secara keseluruhan:

setDialog("open", {
title: 'Judul kotak dialog',
content: 'Isi kotak dialog',
width: 300,
height: 150,
top: false,
left: false,
specialClass: "",
fixed: true,
overlay: false,
buttons: {}
});

Contoh-Contoh Dasar

Kotak dialog dengan judul dan konten berupa kode HTML:

<a href='javascript:setDialog("open", {title: "Judul Dialog", content: "Konten dialog dan &lt;strong&gt;sedikit kode HTML&lt;/strong&gt;"});'>Buka</a>

Menyisipkan elemen <iframe>:

<a href='javascript:setDialog("open", {title: "Judul Dialog", content: "&lt;iframe src=&quot;URL-IFRAME.html&quot;&gt;&lt;/iframe&gt;"});'>Buka</a>

Catatan Penting: Mohon perhatikan mengenai penulisan tanda petik pada pengeksekusian fungsi secara langsung. Jika tanda petik yang digunakan pada atribut HTML adalah petik ganda, gunakan petik tunggal pada pengaturan fungsi setDialog, sebaliknya, jika tanda petik yang digunakan pada atribut HTML adalah petik tunggal, gunakan petik ganda pada pengaturan fungsi setDialog:

<a href="javascript:setDialog('open');">
^ ^ ^ ^
A B B A

<a href='javascript:setDialog("open");'>
^ ^ ^ ^
B A A B

Mengingat konten kotak dialog bisa berukuran sangat panjang, dan juga untuk mengatasi masalah penulisan tanda petik yang sangat rawan kesalahan seperti yang Saya jelaskan di atas, sebenarnya akan lebih baik jika eksekusi fungsi setDialog() dilakukan di luar elemen tersebut dengan cara menghubungkannya melalui ID atau kelas elemen HTML seperti ini:

<a id="dialog-link-1" href="#">Buka</a>
<script>
document.getElementById('dialog-link-1').onclick = function() {
setDialog("open", {
title: 'Judul Dialog',
content: 'Konten dialog super panjaaaaaaaaaaaaaaaannnnggggggg...!!!',
width: 600,
height: 300
});
return false;
};
</script>

Pertama-tama nyatakan kode HTML untuk tombol pemicu kotak dialog dengan ID yang spesifik, setelah itu eksekusi fungsi setDialog dengan cara mengakses elemen melalui document.getElementById()

Anda juga bisa menyisipkan konten dari elemen lain:

<div id="foo" style="display:none;">
<h2>Lorem Ipsum Text</h2>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit,
sed diam <strong>nonummy</strong> nibh euismod
magna aliquam erat volutpat. Ut wisi enim ad minim veniam,
quis nostrud exerci tation ullamcorper suscipit lobortis
nisl ut aliquip ex ea commodo consequat.
Duis autem vel eum iriure dolor in hendrerit in vulputate
velit esse molestie consequat, vel illum dolore eu feugiat
nulla facilisis at vero eros et accumsan et iusto odio
dignissim qui blandit praesent luptatum zzril delenit
augue duis dolore te feugait nulla facilisi.</p>
</div>
<a id="dialog-link-2" href="#">Buka</a>
<script>
document.getElementById('dialog-link-2').onclick = function() {
setDialog("open", {
title: 'Judul Dialog',
content: document.getElementById('foo').innerHTML,
width: 600,
height: 300
});
return false;
};
</script>

Rekayasa Tombol

Masing-masing tombol dapat dibuat melalui konfigurasi buttons dengan nilai berupa objek:

setDialog("open", {
title: 'Judul Kotak Dialog',
content: 'Konten kotak dialog.',
buttons: {
"Yes": function() {
alert("Tombol YES ditekan!");
},
"No": function() {
alert("Tombol NO ditekan!");
}
}
});

Setiap key dari buttons akan mewakili nama/label tombol, dan fungsi yang mengikutinya akan tereksekusi ketika tombol tersebut ditekan:


Kode Sumber

  1. dialog-box.css
  2. dialog-box.js
  3. dialog-box.min.css
  4. dialog-box.min.js

Rabu, 27 Maret 2013

Tentang Tooltip Deskripsi pada Formulir HTML

Gambaran Tooltip pada Formulir HTML

Memaksimalkan kerja selektor adjacent sibling sangat bermanfaat untuk menciptakan komunikasi yang lebih hidup pada saat proses pengisian formulir untuk pengunjung/anggota. Idenya adalah, seorang calon anggota mencoba untuk mengisi formulir. Dan saat kursor teks aktif di dalam elemen formulir tersebut, maka akan muncul pesan singkat di sebelahnya yang menjelaskan tentang apa saja yang harus calon anggota lakukan terhadap formulir yang sedang aktif tersebut.

Ide di atas bisa kita realisasikan hanya dengan cara seperti ini:

HTML

<input type="text">
<label>Mohon ketik setiap awalan dengan huruf kapital</label>

CSS

/* Menyembunyikan label-label formulir */
label {visibility:hidden}

/* Menampilkan label formulir saat elemen input terfokus */
input:focus + label {visibility:visible}

Yang kemudian akan menghasilkan komunikasi antarmuka seperti ini:

Melakukan beberapa pembaharuan pada tampilan elemen <label> untuk membuatnya tampak sebagai tooltip serta menambahkan efek transisi untuk memperlembut proses tampilnya deskripsi/pesan formulir, maka akan membuat komunikasi antarmuka menjadi semakin tegas tanpa harus meminta pengunjung yang sedang mengisi formulir tersebut pergi menuju ke halaman khusus mengenai panduan cara mengisi formulir. Dengan cara ini, pengunjung/calon anggota akan dipandu pada saat yang sama ketika mereka sedang mengisi formulir:

HTML

<div class="form-item">
<input type="text">
<label>Descriptions...</label>
</div>

CSS

/* Form item wrapper */
.form-item {
margin:1em 0 0;
position:relative;
}

/* Form item title */
.form-item strong {
display:block;
margin:0 0 5px;
}

/* Form items */
.form-item input,
.form-item textarea {
display:block;
border:1px solid #ccc;
padding:4px;
margin:0 0;
width:250px;
font:inherit;
line-height:normal;
color:inherit;
-webkit-box-shadow:inset 0 1px 3px -1px rgba(0,0,0,.2);
-moz-box-shadow:inset 0 1px 3px -1px rgba(0,0,0,.2);
box-shadow:inset 0 1px 3px -1px rgba(0,0,0,.2);
}

/* Tooltip */
.form-item label {
display:block;
position:absolute;
bottom:100%;
left:150px;
margin-bottom:2em;
font-size:11px;
font-weight:bold;
color:white;
white-space:nowrap;
line-height:normal;
padding:.6em 1em;
background-color:black;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
visibility:hidden;
opacity:0;
/* Transition effect */
-webkit-transition:all .2s ease-out;
-moz-transition:all .2s ease-out;
-ms-transition:all .2s ease-out;
-o-transition:all .2s ease-out;
transition:all .2s ease-out;
}

/* Tooltip arrow */
.form-item label:after {
content:"";
display:block;
width:0;
height:0;
border:5px solid transparent;
position:absolute;
top:100%;
left:2em;
border-top-color:black;
}

.form-item input:focus,
.form-item textarea:focus {border-color:#aaa}

/* Show tooltip when the form is being focused */
.form-item input:focus + label,
.form-item textarea:focus + label {
visibility:visible;
opacity:1;
margin-bottom:-.5em;
}

Minggu, 24 Maret 2013

Catatan Pembaharuan JQuery

Baru: Fitur baru ditambahkan ke dalam perpustakaan

Deprecated/Usang: Fitur sudah usang, dan akan disingkirkan suatu saat nanti. Alasan sebuah fitur/API menjadi usang biasanya adalah karena beberapa fitur baru yang ditambahkan memiliki kemampuan yang lebih baik dibandingkan dengan fitur yang lama sehingga disarankan untuk beralih ke metode yang baru dan melupakan metode yang lama. Selain itu, perbaikan struktur perpustakaan juga menjadi salah satu penyebab.

Disingkirkan: Fitur benar-benar disingkirkan dari perpustakaan. Anda sudah tidak bisa lagi memakai fitur-fitur yang sudah disingkirkan jika Anda menggunakan JQuery pada versi tersebut.

Versi 1.0

Daftar API untuk JQuery versi 1.0:

  1. .add()
  2. .addClass()
  3. .after()
  4. .ajaxComplete()
  5. .ajaxError()
  6. .ajaxSend()
  7. .ajaxStart()
  8. .ajaxStop()
  9. .ajaxSuccess()
  10. .animate()
  11. .append()
  12. .appendTo()
  13. .attr()
  14. .before()
  15. .bind()
  16. .blur()
  17. .change()
  18. .children()
  19. .click()
  20. .clone()
  21. .css()
  22. .dblclick()
  23. .each()
  24. .empty()
  25. .end()
  26. .error()
  27. event.preventDefault()
  28. event.stopPropagation()
  29. event.target
  30. event.type
  31. .fadeIn()
  32. .fadeOut()
  33. .fadeTo()
  34. .filter()
  35. .find()
  36. .focus()
  37. .get()
  38. .height()
  39. .hide()
  40. .hover()
  41. .html()
  42. .index()
  43. .insertAfter()
  44. .insertBefore()
  45. .is()
  46. jQuery.ajax()
  47. jQuery.boxModel
  48. jQuery.browser
  49. jQuery.each()
  50. jQuery.extend()
  51. jQuery.get()
  52. jQuery.getJSON()
  53. jQuery.getScript()
  54. jQuery.grep()
  55. jQuery.map()
  56. jQuery.merge()
  57. jQuery.noConflict()
  58. jQuery.post()
  59. jQuery.trim()
  60. .keydown()
  61. .keypress()
  62. .keyup()
  63. .length
  64. .load() (AJAX)
  65. .load() (Event)
  66. .mousedown()
  67. .mouseenter()
  68. .mouseleave()
  69. .mousemove()
  70. .mouseout()
  71. .mouseover()
  72. .mouseup()
  73. .next()
  74. .not()
  75. .parent()
  76. .parents()
  77. .prepend()
  78. .prependTo()
  79. .prev()
  80. .ready()
  81. .pushStack()
  82. .remove()
  83. .removeAttr()
  84. .removeClass()
  85. .resize()
  86. .scroll()
  87. .select()
  88. .serialize()
  89. .show()
  90. .siblings()
  91. .size()
  92. .slideDown()
  93. .slideToggle()
  94. .slideUp()
  95. .submit()
  96. .text()
  97. .toggle() (Efek)
  98. .toggle() (Event)
  99. .toggleClass()
  100. .trigger()
  101. .unbind()
  102. .unload()
  103. .val()
  104. .width()
  105. .wrap()

Daftar API untuk selektor:

  1. * All Selector
  2. [name|="value"] Attribute Contains Prefix Selector
  3. [name*="value"] Attribute Contains Selector
  4. [name~="value"] Attribute Contains Word Selector
  5. [name$="value"] Attribute Ends With Selector
  6. [name="value"] Attribute Equals Selector
  7. [name!="value"] Attribute Not Equal Selector
  8. [name^="value"] Attribute Starts With Selector
  9. :button
  10. :checkbox
  11. :checked
  12. parent > child Child Selector
  13. .class Class Selector
  14. ancestor descendant Descendant Selector
  15. :disabled
  16. tag Element Selector
  17. :empty
  18. :enabled
  19. :eq()
  20. :even
  21. :file
  22. :first
  23. :gt()
  24. [attribute] Has Attribute Selector
  25. :hidden
  26. #id ID Selector
  27. :image
  28. :input
  29. :last
  30. :lt()
  31. [name="value"][name2="value2"] Multiple Attribute Selector
  32. selector1, selector2, selectorN Multiple Selector
  33. prev + next Next Adjacent Selector
  34. prev ~ siblings Next Siblings Selector
  35. :not()
  36. :odd
  37. :parent
  38. :password
  39. :radio
  40. :reset
  41. :selected
  42. :submit
  43. :text
  44. :visible

Versi 1.0.4

Baru:

  1. event.metaKey
  2. event.pageX
  3. event.pageY
  4. jQuery.globalEval()

Versi 1.1

Baru:

  1. event.data
  2. jQuery.ajaxSetup()
  3. .one()

Memperbaharui .attr(), dari sebelumnya yang hanya berfungsi untuk mendapatkan nilai atribut elemen menjadi berfungsi untuk mendapatkan atribut dan juga mengeset atribut dalam berbagai cara seperti ini:

// Konsep awal
var src = $('img').attr('src');

// Mengeset nilai atribut
$('img').attr('src', '/path/to/image.jpg');

// Mengeset beberapa atribut sekaligus
$('img').attr({
alt: 'My profile photo',
src: '/path/to/image.jpg'
});

// Concat/menyisipkan nilai atribut baru tanpa menghilangkan nilai atribut awal
$('img').attr('id', function(i, value) {
return value + '-main';
});

Versi 1.1.2

Baru:

  1. .eq()

Versi 1.1.3

Baru:

  1. jQuery.browserjQuery.browser.version
  2. event.which
  3. jQuery.unique()

Versi 1.1.4

Baru:

  1. event.relatedTarget
  2. jQuery.isXMLDoc()
  3. .slice()
  4. :contains()
  5. :first-child
  6. :last-child
  7. :has()
  8. :nth-child()
  9. :only-child

Versi 1.2

Baru:

  1. .andSelf()
  2. .contents()
  3. .dequeue()
  4. .hasClass()
  5. .map()
  6. .nextAll()
  7. .offset()
  8. .position()
  9. .prevAll()
  10. .queue()
  11. .replaceAll()
  12. .replaceWith()
  13. .serializeArray()
  14. .stop()
  15. .triggerHandler()
  16. .wrapAll()
  17. .wrapInner()
  18. :animated
  19. :header
  20. jQuery.inArray()
  21. jQuery.isFunction()
  22. jQuery.makeArray()
  23. jQuery.param()

Versi 1.2.3

Baru:

  1. .data()
  2. .removeData()
  3. jQuery.data()
  4. jQuery.removeData()

Versi 1.2.6

Baru:

  1. event.timeStamp
  2. .innerHeight()
  3. .innerWidth()
  4. .outerHeight()
  5. .outerWidth()
  6. .scrollLeft()
  7. .scrollTop()

Versi 1.3

Baru:

  1. .closest()
  2. .context
  3. .die()
  4. event.currentTarget
  5. event.isDefaultPrevented()
  6. event.isImmediatePropagationStopped()
  7. event.isPropagationStopped()
  8. event.result
  9. event.stopImmediatePropagation()
  10. jQuery.dequeue()
  11. jQuery.fx.off
  12. jQuery.isArray()
  13. jQuery.queue()
  14. jQuery.support
  15. .live()
  16. .pushStack()
  17. .toggleClass()

Menerapkan .toggle() tanpa parameter durasi dan/atau callback akan menghasilkan sikap tampil dan menghilang, tanpa efek:

// Dasar
$('div').click(function() {
$(this).toggle(600);
});

// Penerapan seperti ini bisa dilakukan di JQuery 1.3
$('div').click(function() {
$(this).toggle(); // Lebih baik dibandingkan dengan `$(this).toggle(0)`
});

Deprecated/Usang:

  1. jQuery.boxModel
  2. jQuery.browser

Versi 1.4

Baru:

  1. .clearQueue()
  2. .delay()
  3. .detach()
  4. .first()
  5. .focusin()
  6. .focusout()
  7. .has()
  8. jQuery() (pembaharuan kecil)
  9. jQuery.contains()
  10. jQuery.isEmptyObject()
  11. jQuery.isPlainObject()
  12. jQuery.noop()
  13. jQuery.proxy()
  14. .last()
  15. .nextUntil()
  16. .not()
  17. .parentsUntil()
  18. .prevUntil()
  19. .toArray()
  20. .unwrap()

Pembaharuan .addClass() dan .removeClass(), yang kini bisa menerima fungsi anonim untuk keperluan bypass/jalan pintas seperti ini:

// KASUS: Menambahkan kelas dengan akhiran berupa indeks elemen

// Cara panjang: Menggabungkan `.each()` dengan `.addClass()`
$('ul li').each(function(index) {
$(this).addClass('item-' + index);
});

// Jalan pintas dengan fungsi anonim
$('ul li').addClass(function(index) {
return 'item-' + index;
});

// Contoh untuk `.removeClass()`
$('ul li').removeClass(function(index) {
return $(this).prev().attr('class')
});

Pembaharuan .before(), .after(), .css(), .filter(), .html(), .text(), dan beberapa fungsi yang sudah ada yang kini bisa menerima fungsi anonim untuk keperluan jalan pintas. Selengkapnya baca di dokumentasi. Berikut ini adalah beberapa contoh:

// `.before()`
$('p').before(function(index) {
return '<div class="new-class-' + index + '"></div>';
});

// `.after()`
$('p').after(function(index) {
return '<div class="new-class-' + index + '"></div>';
});

// `.css()`
$('div').css("border", function(i) {
return (i + 1) + 'px solid black';
});

$('div').css({
"border": function(index) {
return (index + 1) + 'px solid black'
},
"background-color": function(index) {
return '#' + (index < 7 ? index : "a") + '00'
}
});

// `.filter()`
$('li').filter(function() {
return $('strong', this).length == 1;
}).css('background-color', 'red');

// dst...

Pembaharuan .offset(), yang kini bisa menerima argumen koordinat untuk mengeset posisi elemen. Sebelumnya hanya berfungsi untuk mendapatkan posisi elemen dari dokumen:

// Sebelum 1.4
var top = $('div').offset().top;
var left = $('div').offset().left;

// 1.4
$('div').offset({
top: 30,
left: 200
});

Versi 1.4.1

Baru:

  1. jQuery.error()
  2. jQuery.parseJSON()

Dan beberapa pembaharuan kecil.

Versi 1.4.2

Baru:

  1. .delegate()
  2. .undelegate()

Versi 1.4.3

Baru:

  1. event.namespace
  2. jQuery.cssHooks
  3. jQuery.fx.interval
  4. jQuery.isWindow()
  5. jQuery.now()
  6. jQuery.type()

Memungkinkan menerapkan easing pada .show(), .hide(), .toggle(), .slideDown(), .slideUp(), .slideToggle(), .fadeIn(), .fadeOut() dan .fadeTo()

// Sebelum 1.4.3
$('div').slideDown(400, function() {
alert("Complete!");
});

// 1.4.3
$('div').slideDown(400, "swing", function() {
alert("Complete!");
});

Versi 1.4.4

Baru:

  1. .fadeToggle()

Versi 1.5

  1. deferred.done()
  2. deferred.fail()
  3. deferred.isRejected()
  4. deferred.isResolved()
  5. deferred.promise()
  6. deferred.reject()
  7. deferred.rejectWith()
  8. deferred.resolve()
  9. deferred.resolveWith()
  10. deferred.then()
  11. jQuery.ajaxPrefilter()
  12. jQuery.ajaxTransport()
  13. jQuery.Deferred()
  14. jQuery.hasData()
  15. jQuery.parseXML()
  16. jQuery.sub()
  17. jQuery.when()

Versi 1.5.1

Pembaharuan jQuery.ajax() dan jQuery.support

Versi 1.6

Baru:

  1. deferred.always()
  2. deferred.pipe()
  3. jQuery.holdReady()
  4. jQuery.map()
  5. .promise()
  6. .prop()
  7. .removeProp()
  8. :focus

Pembaharuan .is() yang kini bisa menerima fungsi anonim untuk melakukan pengecekan yang lebih kompleks dibandingkan dengan pengecekan sebelumnya yang hanya berdasarkan selektor:

// Sebelum 1.6
if ($('#foo').is('input')) {
$('#foo').val("");
} else {
$('#foo').html("");
}

// 1.6
var fooHasChildren = $('#foo').is(function() {
return $(this).children().length;
});
if (fooHasChildren) {
$('#foo').children().addClass('item');
} else {
$('#foo').addClass('empty');
}

Versi 1.7

Baru:

  1. callbacks.add()
  2. callbacks.disable()
  3. callbacks.disabled()
  4. callbacks.empty()
  5. callbacks.fire()
  6. callbacks.fired()
  7. callbacks.fireWith()
  8. callbacks.has()
  9. callbacks.lock()
  10. callbacks.locked()
  11. callbacks.remove()
  12. deferred.notify()
  13. deferred.notifyWith()
  14. deferred.progress()
  15. deferred.state()
  16. event.delegateTarget
  17. jQuery.Callbacks()
  18. jQuery.isNumeric()
  19. .off()
  20. .on()

Pembaharuan .removeAttr(), yang kini bisa menghapus beberapa atribut sekaligus dalam satu kali eksekusi dengan cara menuliskan beberapa nama atribut terpisah oleh spasi:

// Sebelum 1.7
$('img').removeAttr('title').removeAttr('src').removeAttr('alt');

// 1.7
$('img').removeAttr('title src alt');

Deprecated/Usang:

  1. deferred.isRejected() digantikan oleh deferred.state()
  2. deferred.isResolved() digantikan oleh deferred.state()
  3. .die() digantikan oleh .off()
  4. jQuery.sub()
  5. .live() digantikan oleh konteks yang lebih kecil untuk performa yang lebih baik, dengan JQuery .on()
  6. .selector
// Usang
$('a').live("click", function() {
alert('OK!');
});

// Baru
$(document.body).on("click", "a", function() {
alert('OK!');
});

// Disarankan, jika ada induk statis yang lebih dekat, gunakan induk itu sebagai konteks
// Mirip dengan `$('#container').delegate("a", "click", function() {})`
$('#container').on("click", "a", function() {
alert('OK!');
});

Versi 1.8

Baru:

  1. .addBack()
  2. jQuery.parseHTML()

Pembaharuan selektor :eq() yag kini bisa menyeleksi urutan elemen melalui alur mundur dengan menuliskan nilai berupa angka negatif.

Deprecated/Usang:

  1. .andSelf() digantikan oleh .addBack()
  2. deferred.pipe()
  3. .error()
  4. jQuery.error()
  5. .load() (Event)
  6. .size() digantikan oleh kembarannya .length
  7. .toggle() (Event)
  8. .unload()

Versi 1.9

Baru:

  1. .finish()
  2. :first-of-type
  3. :lang
  4. :last-of-type
  5. :nth-last-child()
  6. :nth-last-of-type()
  7. :nth-of-type()
  8. :only-of-type
  9. :root
  10. :target

Pembaharuan untuk .css(), yang sebelumnya hanya bisa mendapatkan satu nilai properti saja setiap kali eksekusi, sekarang kita bisa menggunakan array untuk mengelompokkan properti-properti CSS yang ingin didapatkan, sedangkan hasilnya nanti akan berubah menjadi objek:

// Sebelum 1.9
var width = $('div').css('width');
var height = $('div').css('height');
var paddTop = $('div').css('padding-top');

// 1.9
var props = $('div').css(["width", "height", "padding-top"]); // Hasil => `props = {width:N,height:N,padding-top:N}`
var width = props.width;
var height = props.height;
var paddTop = props["padding-top"];

Disingkirkan:

  1. .toggle() (Event)
  2. jQuery.browser()
  3. .live()
  4. .die()
  5. jQuery.sub()

Sabtu, 23 Maret 2013

Print/Mencetak Sebagian Halaman Saja dengan JavaScript

Gambar untuk dialog pencetakan pada Google Chrome
Mencetak sebagian halaman dengan JavaScript

Berbeda dengan dasar window.print() yang akan mencetak keseluruhan halaman, fungsi ini akan mencetak halaman pada bagian-bagian tertentu saja. Cara kerjanya adalah fungsi ini akan mencetak duplikat konten area di dalam iframe pencetakan.

Dua buah elemen, yaitu <textarea> dan <iframe> dibuat untuk keperluan ini. Area teks digunakan untuk menyimpan kode CSS pencetakan, sedangkan iframe digunakan untuk menampung salinan bagian halaman yang ingin dicetak. Saat salinan bagian halaman tersebut sudah masuk ke dalam iframe yang kita sembunyikan wujudnya, hal yang perlu kita lakukan selanjutnya adalah mencetak konten iframe tersebut, bukan halaman yang sedang aktif:

<textarea id="printing-css" style="display:none;">.no-print{display:none}</textarea>
<iframe id="printing-frame" name="print_frame" src="about:blank" style="display:none;"></iframe>
<script type="text/javascript">
//<![CDATA[
function printDiv(elementId) {
var a = document.getElementById('printing-css').value;
var b = document.getElementById(elementId).innerHTML;
window.frames["print_frame"].document.title = document.title;
window.frames["print_frame"].document.body.innerHTML = '<style>' + a + '</style>' + b;
window.frames["print_frame"].window.focus();
window.frames["print_frame"].window.print();
}
//]]>
</script>

Aktivasi bisa dilakukan dengan cara memicu fungsi printDiv(elementId) pada tautan atau tombol, dimana elementId adalah ID dari area khusus yang ingin dicetak:

<a class="no-print" href="javascript:printDiv('area-1');">Print</a>

...

<div id="area-1">Teks ini akan tercetak di kertas...</div>
<div id="area-2">Tapi tidak untuk teks ini.</div>

Kode CSS pencetakan yang digunakan Saya sarankan adalah kode CSS yang pernah Saya tuliskan di sini, untuk memastikan agar tampilan halaman yang dicetak tertata dengan rapi. Atau kosongkan saja textarea tersebut dan biarkan User Agent Stylesheet pada masing-masing peramban menangani tampilan elemen yang ada.

Alternatif lain, Anda juga bisa menggunakan CSS eksternal jika Anda merasa bahwa textarea tanpa makna di atas hanya akan mengganggu kebersihan kode sumber web Anda:

<iframe id="printing-frame" name="print_frame" src="about:blank" style="display:none;"></iframe>
<script type="text/javascript">
//<![CDATA[
function printDiv(elementId) {
var a = "/path/to/printing-css.css";
var b = document.getElementById(elementId).innerHTML;
window.frames["print_frame"].document.title = document.title;
window.frames["print_frame"].document.body.innerHTML = '<link rel="stylesheet" type="text/css" href="' + a + '">' + b;
window.frames["print_frame"].window.focus();
window.frames["print_frame"].window.print();
}
//]]>
</script>

Hasilnya akan terlihat jelas pada peramban Google Chrome yang notabene memiliki komunikasi pencetakan halaman yang berbeda dengan peramban lainnya.

CSS3 Facebook Chatbox

HTML

.chat-box {
font:normal normal 11px/1.4 Tahoma,Verdana,Sans-Serif;
color:#333;
width:200px; /* Chatbox width */
border:1px solid #344150;
border-bottom:none;
background-color:white;
position:fixed;
right:10px;
bottom:0;
z-index:9999;
-webkit-box-shadow:1px 1px 5px rgba(0,0,0,.2);
-moz-box-shadow:1px 1px 5px rgba(0,0,0,.2);
box-shadow:1px 1px 5px rgba(0,0,0,.2);
}

.chat-box > input[type="checkbox"] {
display:block;
margin:0 0;
padding:0 0;
position:absolute;
top:0;
right:0;
left:0;
width:100%;
height:26px;
z-index:4;
cursor:pointer;
opacity:0;
filter:alpha(opacity=0);
}

.chat-box > label {
display:block;
height:24px;
line-height:24px;
background-color:#344150;
color:white;
font-weight:bold;
padding:0 1em 1px;
}

.chat-box > label:before {content:attr(data-collapsed)}

.chat-box .chat-box-content {
/* padding:10px; */
display:none;
}

/* hover state */
.chat-box > input[type="checkbox"]:hover + label {background-color:#404D5A}

/* checked state */
.chat-box > input[type="checkbox"]:checked + label {background-color:#212A35}
.chat-box > input[type="checkbox"]:checked + label:before {content:attr(data-expanded)}
.chat-box > input[type="checkbox"]:checked ~ .chat-box-content {display:block}

HTML

<div class="chat-box">
<input type="checkbox">
<label data-expanded="Close Chatbox" data-collapsed="Open Chatbox"></label>
<div class="chat-box-content">

<!-- Kode buku tamu dsb... -->

</div>
</div>

Rabu, 20 Maret 2013

Toggle Side Navigation

Image of toggle side navigation example. See the navicon symbol!

HTML

<div id="outer-wrapper">

<section id="nav">
<a href="#nav" id="toggle-nav-btn" title="Show Navigation">&equiv;</a>
<div class="inner">
<ul class="menu">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Archive</a></li>
<li><a href="#">Contact</a></li>
</ul>
...
...
</div>
</section>

<section id="main">
Artikel di sini...
</section>

</div>

CSS

/* Outer wrapper */
#outer-wrapper {
background-color:black;
overflow:hidden;
}

/* Left sidebar */
#nav {
float:left;
width:2.8em;
color:#999;
position:relative;
}

#nav .inner {
padding:1em;
display:none;
}

.menu,
.menu li {
margin:0 0;
padding:0 0;
list-style:none;
}

.menu {margin:2em -1em 1em}

.menu a {
display:block;
font-weight:bold;
color:white;
text-decoration:none;
height:2.4em;
line-height:2.4em;
padding:0 1em;
}

.menu a:hover {background-color:#222}

/* Right sidebar */
#main {
background-color:white;
margin-left:2.8em; /* Same with the `#nav` width */
padding:1em 2em;
}

/* Toggle navigation button */
#toggle-nav-btn {
text-decoration:none;
font-size:200%;
line-height:100%;
color:#666;
display:block;
position:absolute;
top:.2em;
right:.4em;
}

#toggle-nav-btn:hover {color:#888}

/* When the `.menu-is-visible` class applied */
.menu-is-visible #nav {width:200px}
.menu-is-visible #nav .inner {display:block}
.menu-is-visible #main {margin-left:200px} /* Same with the `.menu-is-visible #nav` width */
.menu-is-visible #toggle-nav-btn {color:#aaa}

JavaScript

JavaScript ini digunakan untuk menambahkan dan melepaskan kelas .menu-is-visible pada elemen <body> secara bergantian:

(function() {

var page = document.body,
btn = document.getElementById('toggle-nav-btn');

btn.onclick = function() {

// Toggle the `.menu-is-visible` class to the <body> tag based on `#toggle-btn-nav` click event
page.className = (/(^| )menu-is-visible( |$)/.test(page.className)) ?
page.className.replace(/menu-is-visible/,"") :
page.className + " menu-is-visible";

// Toggle the toggle navigation title too...
this.title = (this.title == "Show Navigation") ?
"Hide Navigation" :
"Show Navigation";

return false;

};

})();

Alternatif Lain

Kita juga bisa menggunakan plugin JQuery Toggle Sidebar untuk menampilkan dan menyembunyikan sidebar, dimana menu navigasi berada di dalam sidebar tersebut. Tapi dalam kasus ini Saya tidak menyarankannya, mengingat menggunakan JavaScript mentah saja masih bisa:

$('#nav').toggleSidebar({
expand: "#main",
defaultHidden: true
});

Senin, 18 Maret 2013

Plugin Feed Rotator untuk Blogger

Image of Blogger Feed Rotator plugin demo

Plugin ini pada dasarnya merupakan perpaduan antara Basic JQuery Image Rotator dengan yang kemudian Saya jadikan sebagai semacam slideshow dengan konten yang akan diperbaharui secara otomatis berdasarkan posting yang sudah terbit. Saya memutuskan untuk membuat ini karena ada beberapa orang yang menanyakan melalui email mengenai bagaimana cara memadukan plugin Image Rotator dengan JSON Blogger.

Plugin JQuery Image Rotator ini juga sudah dimodifikasi hampir secara keseluruhan, khusus untuk diaplikasikan pada widget ini. Beberapa hal yang Saya tambahkan diantaranya adalah navigasi, efek animasi kontainer dan dukungan fungsi callback yang dapat dijalankan pada sesi-sesi tertentu.

Implementasi Dasar

Berikut ini adalah contoh implementasi paling mendasar yang bisa Anda lakukan untuk mengaktifkan plugin:

<link rel="stylesheet" type="text/css" href="http://blogger-json-experiment.googlecode.com/svn/resources/blogger-feed-rotator-plugin/default-style.min.css"/>
<div id="slider-rotator" class="slider-rotator loading"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://blogger-json-experiment.googlecode.com/svn/resources/blogger-feed-rotator-plugin/blogger-feed-rotator.min.js"></script>
<script type="text/javascript">
makeSlider({
url: "http://nama_blog.blogspot.com" // URL blog Anda
});
</script>

Susun file CSS dan plugin Blogger Feed Rotator sesuai dengan urutan yang Saya perlihatkan di atas. Tambahkan juga elemen HTML yang berfungsi untuk menampilkan slideshow sebelum plugin. Setelah itu aktifkan fungsi makeSlider() dengan parameter berupa objek tunggal url yang diisi dengan URL blog Anda. Salin dan tempelkan di dalam formulir elemen halaman HTML/JavaScript. Dengan ini maka Anda sudah bisa menampilkan sebuah slideshow sederhana seperti yang terlihat pada halaman demo di atas.

Daftar Opsi Konfigurasi Dasar

Konfigurasi dasar Saya batasi pada opsi-opsi yang berhubungan dengan pengaturan posting:

{
url: "http://www.dte.web.id", // Your blog URL
numPost: 5, // Number of posts to display
newTabLink: false, // `true` to automatically open link in new window tab
labelName: null, // Show posts in specific label. Specify name of the post label, or `null` to show all posts
showDetail: true, // `false` to hide the blog post title & description
summaryLength: 60, // Length of post summary
titleLength: "auto", // Length of the post title. "auto" by default, or specify number to crop the post title characters and truncate it with `...`
showThumb: true, // `false` to hide the post thumbnails
thumbWidth: 250, // Width of post thumbnail in pixels
squareThumb: true, // Thumbnail mode: Square mode or use the scaled thumbnail with original width and height ratio
noThumb: "images/grey.png", // Fallback thumbnail for no picture posts
showNav: true, // `true` to show both next/prev navigation and navigation number, "next/prev" to show next/prev navigation only, "number" to show navigation number only, `false` to hide the navigation
navText: {
prev: "&lt;", // Label of the previous navigation
next: "&gt;" // Label of the next navigation
},
containerId: "slider-rotator" // HTML element ID that is used to display the slideshow/rotator
}
OpsiKeterangan
urlIsi dengan URL halaman muka blog Anda
numPostDigunakan untuk menentukan jumlah posting yang akan ditampilkan
newTabLinkUbah nilainya menjadi true untuk membuat tautan-tautan di dalam widget ini terbuka di jendela/tab baru saat diklik
labelNameDigunakan untuk menampilkan daftar posting dengan label/kategori tertentu. Nilai null berarti menampilkan semua posting terbaru. Ganti nilainya menjadi nama label untuk menampilkan posting dengan label tertentu. Misalnya labelName:"Foto" untuk menampilkan semua posting yang memiliki label Foto - Contoh
showDetailUbah nilainya menjadi false untuk menyembunyikan judul dan ringkasan posting. Berguna untuk membuat slideshow foto/gambar tanpa tulisan - Contoh
summaryLengthDigunakan untuk membatasi panjang ringkasan posting. Akhir pemotongan ringkasan posting akan digantikan dengan simbol “…”
titleLengthDigunakan untuk membatasi panjang judul posting. Secara normal bernilai "auto". Ubah nilainya dengan angka tertentu untuk memotong panjang judul jika Anda merasa judul posting yang tampil terlalu panjang. Akhir pemotongan judul posting akan digantikan dengan simbol “…”
showThumbUbah nilainya menjadi false untuk menyembunyikan gambar posting. Berguna untuk membuat slideshow teks - Contoh
thumbWidthDigunakan untuk menentukan lebar thumbnail posting dan juga sekaligus menentukan lebar slideshow
squareThumbOpsi untuk memilih mode thumbnail. true untuk menampilkan gambar dengan resolusi /sN-c, false untuk menampilkan gambar dengan resolusi /sN
noThumbIsi dengan URL gambar. Gambar ini akan digunakan sebagai cadangan jika posting yang tampil tidak memiliki gambar
showNavOpsi untuk menampilkan atau menyembunyikan navigasi slideshow. Ubah nilainya menjadi false untuk menyembunyikan navigasi. "number" untuk menampilkan navigasi angka saja. "next/prev" untuk menampilkan navigasi next/prev saja. true untuk menampilkan keduanya
navTextOpsi untuk menentukan label navigasi Next dan Previous
containerIdDigunakan untuk menentukan di mana slideshow akan ditampilkan. Isi dengan ID elemen HTML. Dan jangan lupa untuk menyertakan elemen HTML tersebut sebelum eksekusi plugin.

Pengaturan Animasi

Pengaturan animasi Saya batasi pada opsi-opsi yang berhubungan dengan animasi plugin:

{
interval: 6000, // Slideshow interval
speed: 1000, // Slideshow animation speed
hoverPause: true, // `false` to make the slideshow/rotator keep running on mouse-over
crossFade: false, // `true` if you want to make the slide change effect occurs simultaneously between disappearance and appearance
autoHeight: false, // Animate the rotator to adjust the height of the displayed item
autoSlide: true // `false` to run the slideshow manually (with navigation)
}
OpsiKeterangan
intervalDigunakan untuk menentukan rentang waktu perpindahan setiap item slide dalam satuan milidetik
speedDigunakan untuk menentukan kecepatan animasi dalam satuan milidetik
hoverPauseJika bernilai true maka slideshow akan berhenti bergerak saat pointer mouse berada di atasnya
crossFadeDigunakan untuk menentukan apakah efek penghilangan dan pemunculan slide dilakukan secara bergantian atau bersamaan dalam waktu yang sama tanpa ada jeda
autoHeightJika bernilai true, maka tinggi slideshow akan menyesuaikan diri berdasarkan tinggi item slide yang tampil dengan efek animasi - Contoh
autoSlideUbah nilainya menjadi false untuk membuat slideshow berjalan secara manual melalui aksi klik navigasi

Konfigurasi Lanjutan

Bagian ini Saya khususkan untuk menjelaskan fungsi-fungsi callback dan implementasi lanjutan, khususnya dalam hal modifikasi:

{
onInit: function() {}, // Callback function that will be executed when slideshow is starting
onHide: function(index) {}, // Callback function that will be executed when slideshow is start hiding the slide item
onShow: function(index) {} // Callback function that will be executed after the slideshow showing the slide item
}

onInit

Fungsi ini akan dijalankan pada saat pertama kali slideshow terbentuk. Saat slideshow tampil, maka fungsi ini akan langsung dieksekusi. Contoh penerapan:

makeSlider({
url: "http://nama_blog.blogspot.com",
onInit: function() {
alert('Slideshow ready!');
}
});

Kode di atas akan menampilkan pesan “Slideshow ready!” segera setelah slideshow tampil.

onHide

Fungsi ini akan dijalankan setiap kali item slide mulai menghilang. Saat item slide menghilang, maka fungsi ini akan tereksekusi. Contoh penerapan:

makeSlider({
url: "http://nama_blog.blogspot.com",
onHide: function() {
alert('Mengganti slide...');
}
});

Kode di atas akan menampilkan pesan “Mengganti slide...” setiap kali slide mulai berganti.

onShow

Fungsi ini akan dijalankan setiap kali item slide tampil. Saat item slide tampil, maka fungsi ini akan tereksekusi. Contoh penerapan:

makeSlider({
url: "http://nama_blog.blogspot.com",
onShow: function(index) {
alert('Slide ke ' + index + ' berhasil tampil.');
}
});

Kode di atas akan menampilkan pesan “Slide ke {nomor urut slide yang tampil} berhasil tampil.” setiap kali slide ditampilkan.

Parameter `index`

Parameter index hanya berlaku untuk onHide dan onShow. Parameter ini akan menampilkan indeks slide yang aktif:

makeSlider({
url: "http://nama_blog.blogspot.com",
onInit: function() {
console.log('Slideshow dimulai...');
},
onHide: function(index) {
console.log('Menyembunyikan slide ke ' + index);
},
onShow: function(index) {
console.log('Menampilkan slide ke ' + index);
}
});

Beberapa Contoh Modifikasi Dasar

Mengubah Tampilan

Dengan mengubah ukuran lebar pada gambar dan slideshow, ukuran tinggi pada slideshow dan mengubah tampilan deskripsi/detail posting sebagai caption, maka Anda bisa semakin memperkuat status plugin ini sebagai “slideshow”:

CSS

<link rel="stylesheet" type="text/css" href="http://blogger-json-experiment.googlecode.com/svn/resources/blogger-feed-rotator-plugin/default-style.min.css"/>
<style type="text/css">

/* Custom CSS... */

.slider-rotator {
width:600px;
height:240px;
padding:0 0;
background-color:white;
font:italic normal 12px/1.4 Georgia,Serif;
color:white;
border:1px solid black;
}

.slider-rotator .slider-item {
background-color:white;
height:240px; /* Same with `.slider-rotator` height */
padding:0 0;
}

.slider-rotator h4 {margin-top:0}
.slider-rotator p {margin:2px 0 0}

/* Slider details as caption */
.slider-rotator .detail-wrapper {
position:absolute;
top:auto;
right:0;
bottom:0;
left:0;
background-color:black;
background-color:rgba(0,0,0,.9);
padding:.5em 1em;
z-index:4;
}

</style>

JavaScript

makeSlider({
url: "http://nama_blog.blogspot.com", // Your blog URL
thumbWidth: 600 // Thumbnail width in pixels (same with slideshow width)
});

Memanfaatkan Fungsi Callback untuk Menganimasikan Caption

Dengan memanfaatkan parameter index pada onHide dan onShow, Anda bisa memberikan efek animasi pada caption slide yang aktif setiap kali slide berganti:

makeSlider({
url: "http://nama_blog.blogspot.com", // Your blog URL
thumbWidth: 600, // Thumbnail width in pixels (same with slideshow width)
containerId: "slider-rotator",
// Hide all captions on initiation...
onInit: function() {
$('#' + this.containerId).find('.detail-wrapper').hide();
},
// Hide the caption with `.slideUp()` effect when the slide item hides
onHide: function() {
$('#' + this.containerId).find('.detail-wrapper').slideUp();
},
// Show the caption with `.slideDown()` effect when the slide item appears
onShow: function(index) {
$('#' + this.containerId).children().eq(index).find('.detail-wrapper').slideDown();
}
});

Menampilkan Beberapa Slideshow Sekaligus

Karena eksekusi plugin ini dilakukan di luar, maka ini memungkinkan Anda untuk menampilkan beberapa slideshow sekaligus dalam satu halaman. Pertama-tama buatlah beberapa elemen HTML yang dibutuhkan untuk memuat slideshow:

<div id="container-1" class="slider-rotator loading"></div>
<div id="container-2" class="slider-rotator loading"></div>
<div id="container-3" class="slider-rotator loading"></div>

Kemudian jalankan fungsi makeSlider() sebanyak jumlah kontainer yang dibuat. Tentukan juga konfigurasi containerId yang berbeda-beda berdasarkan ID kontainer:

makeSlider({
url: "http://nama_blog-1.blogspot.com",
containerId: "container-1"
});
makeSlider({
url: "http://nama_blog-2.blogspot.com",
containerId: "container-2"
});
makeSlider({
url: "http://nama_blog-3.blogspot.com",
containerId: "container-3"
});

Mengatur Plugin dan Melihat Demo Secara Langsung

Saya sudah menyertakan halaman konfigurasi plugin yang bisa Anda gunakan untuk melakukan pengaturan dan melihat hasilnya secara langsung di sini:

Daftar Contoh

  1. Demo 1: Basic
  2. Demo 2: Show Posts under Category “Widget”
  3. Demo 3: About Thumbnails
  4. Demo 4: Thumbnail Only
  5. Demo 5: Text Only
  6. Demo 6: Multiple Feed Loading
  7. Demo 7: Casual Slideshow
  8. Demo 8: Example Callback Function to Animate the Slideshow Caption (1)
  9. Demo 9: Example Callback Function to Animate the Slideshow Caption (2)

Minggu, 10 Maret 2013

Mendapatkan Tinggi dan Lebar Layar Monitor

Versi JQuery

var x = $(window).width(),
y = $(window).height();

// Tampilkan lebar dan tinggi layar dalam kotak pesan
alert('Lebar layar: ' + x + '; Tinggi layar: ' + y);

Versi JavaScript Murni

var w = window,
d = document,
e = d.documentElement,
g = d.body,
x = w.innerWidth || e.clientWidth || g.clientWidth, // x = Lebar layar
y = w.innerHeight || e.clientHeight || g.clientHeight; // y = Tinggi layar

// Tampilkan lebar dan tinggi layar dalam kotak pesan
alert('Lebar layar: ' + x + '; Tinggi layar: ' + y);

Kapan Harus Menggunakan px, % dan em?

Meskipun, kebanyakan dari kita masih bertahan dengan satuan piksel saat menentukan ukuran-ukuran elemen HTML, namun di sini Saya ingin mencoba untuk membuka mata Anda lebih lebar lagi agar Anda bisa mengerti betul mengapa satuan-satuan relatif seperti em dan % dalam banyak kasus lebih efisien dan logis dibandingkan dengan satuan piksel (px) yang selama ini paling sering dipakai karena ukuran mereka yang sangat akurat.

Ketika % Menjadi Lebih Baik

Persentase merupakan satuan yang tidak dihitung berdasarkan ukuran elemen itu sendiri, melainkan dihitung berdasarkan perbandingan ukuran elemen tersebut terhadap ukuran induknya.

Satuan % Dalam Sistem Grid

Dimulai dari kasus sistem grid, atau katakanlah kolom-kolom web Anda. Di sini kita memiliki tiga elemen HTML untuk merancang sebuah susunan/layout situs sederhana. Terdiri dari pembungkus utama dan dua buah kolom. Salah satu kolom merupakan area artikel, dan satunya lagi merupakan area sidebar:

<div class="col-group">
<div class="left-col">Artikel...</div>
<div class="right-col">Sidebar...</div>
</div>

Seseorang dengan sudut pandang piksel akan membuat layout dua kolom dengan pengukuran seperti ini:

.col-group {
width:600px;
overflow:hidden;
}

.left-col {
width:400px;
float:left;
}

.right-col {
width:200px; /* 600 - 400 */
float:right;
}

CSS di atas akan menghasilkan layout dua kolom dengan ukuran pembungkus utama selebar 600 piksel. Kolom kiri selebar 400 piksel dan kolom kanan selebar 200 piksel. Ukuran layout yang sangat tepat dan tidak mungkin bisa diragukan akurasinya. Tapi sayangnya, layout piksel seperti ini hanya akan menimbulkan kendala saat Anda mencoba untuk memodifikasi atau memperbaharui lebar layout tersebut di masa depan. Misalnya, suatu saat Anda ingin mengubah lebar layout menjadi 1000 piksel seperti ini:

.col-group {
width:1000px;
overflow:hidden;
}

Pada saat yang sama Anda tentu harus memperbaharui lebar kolom kiri dan kolom kanan:

.left-col {
width:600px; /* 400 + ((1000-600)/2) */
float:left;
}

.right-col {
width:400px; /* 1000 - 600 */
float:right;
}

Tidak praktis, tidak hemat waktu dan membutuhkan perhitungan baru setiap kali ukuran layout diperbaharui. Jika susunan halaman hanya terdiri dari dua buah kolom sederhana seperti contoh di atas mungkin tidak begitu menjadi masalah, tapi bagaimana jika jumlah kolomnya banyak?

Bandingkan dengan saat Anda mengatur lebar kolom menggunakan satuan persentase:

.col-group {
width:600px; /* lebar pembungkus utama */
overflow:hidden;
}

.left-col {
width:66.6%; /* (400/600) x 100 */
float:left;
}

.right-col {
width:33.4%; /* 100 - 66.6 */
float:right;
}

Meskipun beberapa orang (termasuk Saya) masih merasa sedikit kesulitan ketika mencoba mengubah sudut pandang satuan piksel ke persen, tapi hasil akhirnya nanti Saya jamin akan lebih stabil dan lebih mudah diperbaiki/diperbaharui (maintainable) saat kita menggunakan satuan persen. Karena yang menjadi standar ukuran di sini hanya ada satu, yaitu pada elemen terluar saja. Sedangkan ukuran lebar anak-anak kolom di dalamnya yang menggunakan satuan persen akan secara otomatis mengikuti ukuran berdasarkan perbandingan lebar induknya, betapapun kita mengubah lebar pembungkus luar kolom-kolom tersebut.

Demonstrasi di halaman ini mungkin bisa sedikit menjelaskan bagaimana persen dan piksel bekerja pada sistem grid:

Satuan % Dalam Elemen Heading/Judul Artikel

Elemen-elemen heading h1, h2, h3, h4, h5 dan h6 akan lebih baik jika diatur menggunakan satuan persen. Walaupun beberapa ada juga yang menggunakan satuan em. Sebenarnya itu tidak masalah, karena baik em maupun % keduanya sama-sama merupakan satuan relatif. Yang penting cobalah untuk menghilangkan kebiasaan menggunakan satuan piksel dalam menentukan ukuran elemen heading. Ini berhubungan dengan pengaplikasian media queries yang lebih efisien dan juga kemudahan di dalam pengaturan ukuran judul berdasarkan ukuran teks utama. Sebuah contoh sederhana, di sini Anda mencoba menentukan ukuran elemen-elemen heading dengan satuan piksel:

body {
font-size:13px;
font-family:Arial,Sans-Serif;
}

h1 {font-size:28px}
h2 {font-size:25px}
h3 {font-size:22px}
h4 {font-size:19px}
h5 {font-size:16px}
h6 {font-size:13px}

Mengapa satuan piksel buruk dalam hal ini? Itu karena satuan piksel hanya akan membuat Anda melakukan ekstra perubahan ukuran teks dalam media queries perangkat seluler. Dimulai dari ukuran teks dasar yang dideklarasikan di dalam selektor body sampai ke ukuran teks pada elemen heading level 6:

@media (max-width:360px) {
body {font-size:11px}
h1 {font-size:26px}
h2 {font-size:23px}
h3 {font-size:20px}
h4 {font-size:17px}
h5 {font-size:14px}
h6 {font-size:11px}
}

Pola penyusutan teks akan berbeda jika Anda menggunakan satuan persentase saat menentukan ukuran judul artikel. Saat menggunakan satuan persentase, maka ukuran judul-judul artikel akan menyesuaikan diri berdasarkan perbandingan ukuran teks dasar saat itu, yaitu 13px (Sebagai contoh: 200% dari 13 piksel adalah 26 piksel):

body {
font-size:13px;
font-family:Arial,Sans-Serif;
}

h1 {font-size:200%}
h2 {font-size:180%}
h3 {font-size:160%}
h4 {font-size:140%}
h5 {font-size:120%}
h6 {font-size:100%}

Karena Anda menggunakan satuan persentase pada semua judul artikel, maka saat Anda memperkecil ukuran teks pada perangkat seluler, yang perlu Anda lakukan hanya memperkecil ukuran teks dasarnya saja, yaitu pada elemen <body>. Ukuran teks pada semua judul artikel nantinya akan menyusut menyesuaikan diri berdasarkan persentase terhadap ukuran teks utama yang sudah mengecil:

@media (max-width:360px) {
body {font-size:11px}
}

Menggunakan satuan persentase untuk elemen heading juga akan mempermudah Anda di dalam memperbaharui ukuran teks artikel. Anda tidak perlu memperbaharui semua ukuran judul artikel. Cukup perbaharui ukuran teks dasarnya saja. Selebihnya akan secara otomatis mengikuti skala yang diterapkan.

Ketika em Menjadi Lebih Baik

em adalah satuan yang dihitung berdasarkan ukuran teks di sekitarnya. Pada dasarnya 1em sama dengan 16 piksel. Itu jika Anda tidak menentukan ukuran teks pada elemen manapun. Tapi jika —misalnya— Anda menentukan ukuran teks sebesar 30 piksel pada <body>, maka 1em di area <body> akan setara dengan 30px (1em = Ukuran Teks di Sekitarnya).

Satuan em dalam line-height dan margin Paragraf

Satuan em sangat bermanfaat untuk menentukan ukuran dan jarak elemen yang terkait erat dengan ukuran teks. Misalnya margin paragraf dan jarak antar baris teks.

Contoh Kasus: Dua buah kolom dengan elemen paragraf di dalamnya mendapatkan ukuran teks sebesar 13 piksel yang diturunkan dari <body>. Paragraf di dalam kolom pertama menggunakan satuan piksel untuk mengatur margin dan line-height, sedangkan paragraf yang berada di dalam kolom ke dua akan menggunakan satuan em:

HTML

<div class="one">
<p>Lorem ipsum dolor sit amet...</p>
<p>Lorem ipsum dolor sit amet...</p>
</div>
<div class="two">
<p>Lorem ipsum dolor sit amet...</p>
<p>Lorem ipsum dolor sit amet...</p>
</div>

CSS

body {font-size:13px} /* 1em = 13px */

.one p {
line-height:18px;
margin:13px 0;
}

.two p {
line-height:1.384615384615385em; /* 13 x 1.384615384615385 = 18.000000000000007 */
margin:1em 0;
}

Hasil akhirnya, ukuran dan jarak antar baris paragraf akan tampak sama:

PX & EM
Sebelah kiri: Menggunakan piksel. Sebelah kanan: Menggunakan em.

Masalah akan muncul ketika Anda mencoba untuk memperbesar/memperkecil ukuran teks:

PX & EM
Sebelah kiri: Menggunakan piksel. Sebelah kanan: Menggunakan em.

Kolom sebelah kiri: Karena kita menggunakan satuan piksel, maka ketika ukuran teks berubah (dalam kasus ini: bertambah besar) jarak antar baris dan margin paragraf tetap sempit dan tidak berubah, bertahan pada ukuran 18px dan 13px. Membuat tulisan menjadi semakin sulit untuk dibaca.

Kolom sebelah kanan: Karena kita menggunakan satuan em, maka skala ukuran tinggi baris dan margin antarparagraf akan menyesuaikan diri berdasarkan ukuran teks.

Dalam Semua Elemen yang Terkait dengan Ukuran Teks

Pada intinya satuan em cocok untuk diterapkan pada semua hal yang berhubungan dengan ukuran teks. Semua hal yang membutuhkan ukuran dengan perbandingan yang tepat terhadap teks. Contoh paling sederhana ada pada desain tombol:

CSS3 Buttons
Desain tombol dengan satuan em pada padding dan border-radius akan menciptakan skala tombol yang lebih ideal dibandingkan px.

Ketika px Menjadi Lebih Baik

Satuan piksel dihitung berdasarkan ukuran lebar dan tinggi satu unit piksel pada layar. Oleh karena itu satuan piksel akan sesuai jika diterapkan pada elemen-elemen yang membutuhkan ukuran yang akurat dan tidak terpengaruh dengan ukuran elemen di sekitarnya. Misalnya lebar dan tinggi ikon, tebal border, blur bayangan, posisi latar dan ukuran teks utama. Ukuran teks utama biasanya dideklarasikan pada elemen <body> atau <html>.


Masalah Akurasi Ukuran pada Satuan Non-Piksel

Tidak bisa dipungkiri bahwa ukuran persentase dan relatif teks tidak akan bisa seakurat ukuran piksel. Tapi memangnya siapa yang peduli dengan itu? Kita, para penikmat desain dan tipografi pada dasarnya tidak pernah peduli mengenai seberapa besar tepatnya, ukuran huruf dan judul artikel yang sedang kita baca. Kita tidak peduli berapa piksel ukuran judul dan teks yang sedang kita baca. Karena yang kita pedulikan selama ini adalah proporsi yang ideal. Tipografi tidak begitu mementingkan akurasi, tipografi lebih mementingkan proporsi. Saya pikir kita tidak perlu khawatir untuk memulai beralih dari piksel dalam kasus-kasus tertentu menuju satuan-satuan relatif yang lebih mudah dipelihara dan responsif terhadap skala di sekitarnya.

Jumat, 08 Maret 2013

Menampilkan Total Waktu Memuat Halaman

<div id="load-time">Calculating page load time...</div>
<script type="text/javascript">
var showTime = document.getElementById('load-time'),
startTime = (new Date()).getTime(); // Get the time before `window.onload`

window.onload = function() {
// Get the current time in `window.onload`
var endTime = (new Date()).getTime();
// Now use the `startTime` and `endTime` to calculate the seconds
// And show the result inside `#load-time`
showTime.innerHTML = "Page load time = " + ((endTime-startTime)/1000) + " sec(s).";
};
</script>

Rabu, 06 Maret 2013

JavaScript Custom Vertical Scrollbar

Cara kerja manipulasi scrollbar ini bukan dengan cara menghilangkan scrollbar yang asli dan menggantinya dengan yang baru, melainkan hanya menyembunyikan scrollbar asli tersebut di balik/di belakang scrollbar palsu yang kita ciptakan di atasnya. Jika Anda menghilangkan warna latar pada scrollbar buatan ini, maka elemen scrollbar yang asli akan dengan mudah terlihat:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript Custom Vertical Scrollbar</title>
<style type="text/css">
.scroll-area-wrapper {
width:80%;
margin:50px auto;
}
.scroll-area {
overflow:auto;
padding-right:1em;
position:relative;
}
.scrollbar-track,
.scrollbar-arrow-up,
.scrollbar-arrow-down,
.scrollbar-thumb {position:absolute}
/* scrollbar track */
.scrollbar-track {
background-color:#ccc;
top:0;
right:0;
bottom:0;
}
/* scrollbar thumb */
.scrollbar-thumb {
background-color:#eee;
cursor:pointer;
position:absolute;
right:0;
}
/* up & down arrow */
.scrollbar-arrow-up,
.scrollbar-arrow-down {
background-color:#eee;
cursor:pointer;
right:0;
}
.scrollbar-arrow-up {top:0}
.scrollbar-arrow-down {bottom:0}
.scrollbar-arrow-up:before,
.scrollbar-arrow-down:before {
content:"";
display:block;
position:absolute;
border:4px solid transparent;
left:50%;
margin-left:-4px;
}
.scrollbar-arrow-up:before {
border-bottom-color:#aaa;
top:50%;
margin-top:-6px;
}
.scrollbar-arrow-down:before {
border-top-color:#aaa;
bottom:50%;
margin-bottom:-6px;
}
/* hover state */
.scrollbar-arrow-up:hover,
.scrollbar-arrow-down:hover,
.scrollbar-thumb:hover {background-color:#ddd}
</style>
<script type="text/javascript">
var ssb = {
aConts: [],
mouseY: 0,
N: 0,
asd: 0, /* active scrollbar element */
sc: 0,
sp: 0,
to: 0,

// constructor
scrollbar: function (cont_id) {
var cont = document.getElementById(cont_id);

// perform initialization
if (! ssb.init()) return false;

var cont_outer = document.createElement('div');
cont_outer.style.overflow = 'hidden';
cont_outer.style.position = 'relative';
cont.parentNode.appendChild(cont_outer);
cont_outer.appendChild(cont);
cont_outer.style.height = cont.offsetHeight + 'px';
cont.style.position = 'relative';
cont.style.top = '0px';
cont.style.height = '100%';

// adding new container into array
ssb.aConts[ssb.N++] = cont;

cont.sg = false;

//creating scrollbar child elements
cont.st = this.create_div('scrollbar-track', cont, cont_outer);
cont.sb = this.create_div('scrollbar-thumb', cont, cont_outer);
cont.su = this.create_div('scrollbar-arrow-up', cont, cont_outer);
cont.sd = this.create_div('scrollbar-arrow-down', cont, cont_outer);

// on mouse down processing
cont.sb.onmousedown = function (e) {
if (! this.cont.sg) {
if (! e) e = window.event;

ssb.asd = this.cont;
this.cont.yZ = e.screenY;
this.cont.sZ = cont.scrollTop;
this.cont.sg = true;

}
return false;
};

// on mouse down on free track area - move our scroll element too
cont.st.onmousedown = function (e) {
if (! e) e = window.event;
ssb.asd = this.cont;

ssb.mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
for (var o = this.cont, y = 0; o !== null; o = o.offsetParent) y += o.offsetTop;
this.cont.scrollTop = (ssb.mouseY - y - (this.cont.ratio * this.cont.offsetHeight / 2) - this.cont.sw) / this.cont.ratio;
this.cont.sb.onmousedown(e);
};

// onmousedown events
cont.su.onmousedown = cont.su.ondblclick = function (e) { ssb.mousedown(this, -1); return false; };
cont.sd.onmousedown = cont.sd.ondblclick = function (e) { ssb.mousedown(this, 1); return false; };

// onmouseout events
cont.su.onmouseout = cont.su.onmouseup = ssb.clear;
cont.sd.onmouseout = cont.sd.onmouseup = ssb.clear;

// onscroll - change positions of scroll element
cont.ssb_onscroll = function () {
this.ratio = (this.offsetHeight - 2 * this.sw) / this.scrollHeight;
this.sb.style.top = Math.floor(this.sw + this.scrollTop * this.ratio) + 'px';
};

// scrollbar width
cont.sw = 18;

// start scrolling
cont.ssb_onscroll();
ssb.refresh();

// binding own onscroll event
cont.onscroll = cont.ssb_onscroll;
return cont;
},

// initialization
init: function () {
if (window.oper || (! window.addEventListener && ! window.attachEvent)) { return false; }

// temp inner function for event registration
function addEvent (o, e, f) {
if (window.addEventListener) { o.addEventListener(e, f, false); ssb.w3c = true; return true; }
if (window.attachEvent) return o.attachEvent('on' + e, f);
return false;
}

// binding events
addEvent(window.document, 'mousemove', ssb.onmousemove);
addEvent(window.document, 'mouseup', ssb.onmouseup);
addEvent(window, 'resize', ssb.refresh);
return true;
},

// create and append div finc
create_div: function(c, cont, cont_outer) {
var o = document.createElement('div');
o.cont = cont;
o.className = c;
cont_outer.appendChild(o);
return o;
},

// do clear of controls
clear: function () {
clearTimeout(ssb.to);
ssb.sc = 0;
return false;
},

// refresh scrollbar
refresh: function () {
for (var i = 0, N = ssb.N; i < N; i++) {
var o = ssb.aConts[i];
o.ssb_onscroll();
o.sb.style.width = o.st.style.width = o.su.style.width = o.su.style.height = o.sd.style.width = o.sd.style.height = o.sw + 'px';
o.sb.style.height = Math.ceil(Math.max(o.sw * 0.5, o.ratio * o.offsetHeight) + 1) + 'px';
}
},

// arrow scrolling
arrow_scroll: function () {
if (ssb.sc !== 0) {
ssb.asd.scrollTop += 6 * ssb.sc / ssb.asd.ratio;
ssb.to = setTimeout(ssb.arrow_scroll, ssb.sp);
ssb.sp = 32;
}
},

/* event binded functions: */
// scroll on mouse down
mousedown: function (o, s) {
if (ssb.sc === 0) {
ssb.asd = o.cont;
ssb.sc = s;
ssb.sp = 400;
ssb.arrow_scroll();
}
},

// on mouseMove binded event
onmousemove: function(e) {
if (! e) e = window.event;
// get vertical mouse position
ssb.mouseY = e.screenY;
if (ssb.asd.sg) ssb.asd.scrollTop = ssb.asd.sZ + (ssb.mouseY - ssb.asd.yZ) / ssb.asd.ratio;
},

// on mouseUp binded event
onmouseup: function (e) {
if (! e) e = window.event;
var tg = (e.target) ? e.target: e.srcElement;
if (ssb.asd && document.releaseCapture) ssb.asd.releaseCapture();

document.onselectstart = '';
ssb.clear();
ssb.asd.sg = false;
}

};
</script>
</head>
<body>

<div class="scroll-area-wrapper">
<div class="scroll-area" id="scroll-area-1" style="height:400px;">
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.</p>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.</p>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.</p>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.</p>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.</p>
</div>
</div>
<script type="text/javascript">
ssb.scrollbar('scroll-area-1'); // scrollbar initialization with element ID
</script>

</body>
</html>

Sabtu, 02 Maret 2013

Eksperimen Infinite Scroll dengan JSON Blogger

Blogger Infinite Scroll Experiment
Blogger Infinite Scroll Experiment

Eksperimen membuat infinite scroll dengan JSON Blogger. Sebuah sistem yang akan memperbaharui jumlah item posting setiap kali pengguna mencapai batas akhir layar ketika pengguna sedang menggulung halaman. Metode ini sebenarnya sudah pernah Saya tuliskan di sini. Saya mengganti kondisional batas akhir yang tadinya berupa perbandingan antara tinggi kontainer dengan batas akhir gulungan area seperti ini:

container.onscroll = function() {
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
myFunction();
}
};

Menjadi berdasarkan perbandingan antara tinggi layar dengan batas akhir gulungan layar seperti ini:

window.onscroll = function() {
if (navigator.userAgent.toLowerCase().indexOf("chrome") > -1 || navigator.userAgent.toLowerCase().indexOf("safari") > -1) {
if (document.documentElement.scrollHeight == (document.body.scrollTop + document.documentElement.clientHeight)) {
myFunction();
}
} else {
if (document.documentElement.scrollHeight == (document.documentElement.scrollTop + document.documentElement.clientHeight)) {
myFunction();
}
}
};

Selengkapnya mengenai potongan kode untuk mengecek batas akhir gulungan layar bisa Anda baca di sini.

Silakan dipelajari dan dimodifikasi sesuka hati Anda dengan mengikuti persetujuan yang Saya tuliskan di dalam kode sumbernya. Eksperimen ini akan diperbaharui jika memang ada yang perlu diperbaharui. Begitu.