ปี2020, จริงๆ เราไม่ต้องใช้ jQuery แล้วก็ได้นะ

jQuery เป็นหนึ่งใน JavaScript Library ที่โด่งดังมาก (เมื่อ 10 ปีที่แล้ว) เรียกว่าในยุคนั้นแทบจะทุกเว็บจะต้องมีการติดตั้ง jQuery เอาไว้อย่างแน่นอน

แต่เมื่อยุคสมัยเปลี่ยนไป เบราเซอร์ใหม่ๆ ไม่มีปัญหาการรัน JavaScript ตั้งแต่ ES6 ขึ้นไปแล้ว การใช้งาน jQuery จึงลดน้อยลงเรื่อยๆ

แต่ก็มีบางโปรเจคเหมือนกัน ที่เราต้องเข้าไปแก้โค้ดเก่าๆ ซึ่งเขียนด้วย jQuery เอาไว้ ในบทความนี้จะมาเปรียบเทียบว่าทุกวันนี้เราสามารถแปลง jQuery ให้กลายเป็น Vanilla JavaScript (JavaScript เพียวๆ แบบไม่ต้องลง lib อะไรเพิ่ม เหมือนกับการกินไอติมรสเบสิกมากๆ แบบวนิลา) ได้เลยแทบจะทุกคำสั่งที่ใช้บ่อยๆ แล้วล่ะ

ในบทความนี้ก็เลยลองเอาคำสั่ง jQuery ที่ใช้บ่อยๆ มาลองเทียบดู ว่าถ้าอยากเขียนแบบนี้โดยไม่ใช้ jQuery เราจะต้องทำยังไง

Ajax

ในยุค jQuery กำลังรุ่งเรือง การเรียกข้อมูลจาก API ด้วย HTTP Request หรือที่ในยุคนั้นนิยมเรียกว่า Ajax ถ้าเขียนด้วย JavaScript แบบธรรมดาจะยากมาก

การที่ jQuery มีฟังก์ชันสำหรับเรียกใช้ Ajax ง่ายๆ ด้วยเป็นหนึ่งในเหตุผลที่ทำให้มันได้รับความนิยม ถึงขนาดว่าบางครั้งโหลด jQuery ติดเข้าไปเพราะต้องการใช้ฟังก์ชัน ajax() แค่นั้นเลยก็มีนะ

$.ajax({
  method: 'GET',
  url: '/api/data',
  data: {
    page: 1,
  },
  dataType: 'json',
  success: function(data){
    console.log('Success:', data)
  },
  error: function(error){
    console.error('Error:', error)
  },
})

แต่ข้อเสียของ jQuery.ajax() ก็คือตอนนี้ฝั่ง vanilla มีทั้ง fetch() หรือไลบรารี่อย่าง Axios ให้ใช้งาน ซึ่งทั้งสองวิธีนี้ถูกสร้างมาด้วย JavaScript ยุคใหม่โดยใช้ Promise แล้ว ไม่เหมือนกับ jQuery ที่ยังใช้สไตล์ callback function อยู่เลย

//fetch API
fetch('/api/data', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    page: 1,
  }),
})
.then(response => response.json())
.then(data => {
  console.log('Success:', data)
})
.catch((error) => {
  console.error('Error:', error)
})

//Axios
axios.get('/api/data', {
  params: {
    pafe: 1
  }
})
.then(function (response) {
  console.log('Success:', data)
})
.catch(function (error) {
  console.error('Error:', error)
})

Query Elements

First Match Element

หา element ตัวแรก

//jQuery
$('.ele').first()
$('.ele:first')

//Vanilla
document.querySelector('.box')

All Elements

หา element ทุกตัวที่ตรงกับ selector

//jQuery
$('.ele')

//Vanilla
document.querySelectorAll('.box')

สำหรับ jQuery แล้วการหา element แค่ 1 ตัวหรือหาทั้งหมด ผลที่ได้ไม่ต่างกันนั้นคืออ็อบเจคของ jQuery แต่สำหรับวนิลาแล้ว querySelector จะให้ค่ามาเป็น Element ส่วน querySelectorAll จะให้ค่าเป็น Array of Elements นะ

Nested Element

การหา element ภายใน element อีกทีหนึ่ง

//jQuery
let container = $('#container')
container.find('.box')

//Vanilla
let container = document.querySelector('.container')
container.querySelector('.box')

Action on Elements

สำหรับการสั่ง action อะไรบางอย่างกับ element ที่ได้มา ถ้าเลือกออกมาแค่ 1 element อันนี้ไม่ยาก

//jQuery
$(".box").first().doSomething()

//Vanilla
document.querySelector(".box").doSomething()

ปัญหาจะเกิดขึ้นเมื่อเรา select all กับ element ทุกตัว ซึ่งเรื่องนี้น่าจะง่ายกว่าถ้าเราใช้ jQuery เพราะการสั่ง action ของ jQuery นั้นจะวนลูปทำให้กับ element ทุกตัวอยู่แล้ว

//jQuery
$(".box").doSomething()

//Vanilla
document.querySelectorAll(".box").forEach(box => { 
    /* doSomething */ 
})

แต่สำหรับวนิลานั้น ค่าที่ได้ออกมาเป็น Array แปลว่าถ้าเราอยากสั่งให้มันทำอะไรให้ครบทุกตัว เราจะต้องวนลูปเอาเอง

เช่นสมมุติว่าเราอยาก hide ทุกอย่างที่มีคลาส box ถ้าเป็น jQuery ก็สั่งง่ายๆ เลยคือ .hide() แต่ถ้าเป็นวนิลาก็วนลูปยาวไป แล้วก็กำหนดที่ style ให้ display=none แทน

//jQuery
$(".box").hide()

//Vanilla
document.querySelectorAll(".box").forEach(box => { 
    box.style.display = "none" 
})

DOM Traversal

การวิ่งขึ้นๆ ลงๆ ใน DOM (โครงสร้างชั้นของ HTML ที่อยู่ในรูปแบบของ Tree) อันนี้ไม่ต่างกันมากเท่าไหร่ แค่คำสั่งของ jQuery จะสั้น/เขียนกระชับกว่านิดหน่อย

//jQuery
box.next()
box.prev()
box.parent()
box.children()
box.siblings()

//Vanilla
box.nextElementSibling
box.previousElementSibling
box.parentElement
box.children
box.parentNode.children.filter(el => el !== box) 

ตัวที่วนิลาไม่มีคือ siblings() ซึ่งเป็นคำสั่งเอาไว้เลือก Element ที่อยู่เลเวลเดียวกับเราทั้งหมด (แต่ไม่รวม Element ตัวเอง) ดังนั้นเลยต้องประยุกต์นิดหน่อยโดยวิ่งขึ้นไปที่ parent แล้วเลือก child ทุกตัวที่ไม่ใช่ตัวมันเองแทน

Event Handling

การกำหนด Event Listener แทบจะไม่ต่างกันแล้ว เพราะตอนหลังวนิลาก็คำสั่ง addEventListener ให้ใช้งานแล้ว

//jQuery
ele.click(function(event){})
ele.on('click', function(event){})
ele.on('mouseenter', function(event){})
ele.on('keyup', function(event){})
ele.off('click', function(event){})

//Vanilla
ele.addEventListener('click', (e) => {})
ele.addEventListener('mouseenter', (e) => {})
ele.addEventListener('keyup', (e) => {})
ele.removeEventListener('click', (e) => {})

Delegate

การดีลีเกตคือการกำหนด Event Listener ที่ Element ชั้นนอกแทนที่จะกำหนดที่ตัวมันเองตามปกติ

เช่นหากเราต้องการสร้าง Listener ที่ทำงานเมื่อกด <li> ในโครงสร้างแบบนี้

<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>

เราก็ไปกำหนดให้ Listener อยู่ที่ <ul> แทน

สาเหตุที่ต้องทำแบบนี้มักจะเกิดกับกรณีที่ <li> นั้นอาจจะโดนสร้างแบบ dynamic โดย script ส่วนอื่น ทำให้อาจจะมี li เกิดขึ้นมาใหม่ได้เรื่อยๆ การไล่สั่งให้มัน Listener ทีละครั้งๆ ไปเป็นอะไรที่จัดการยากกมา เลยไปสั่งที่ ul แทน (แล้วพอเกิด Event ก็ให้ ul ส่งไปบอก li ภายในอีกที)

สำหรับวนิลาไม่มีคำสั่งให้ทำดีลีเกตได้เหมือน jQuery ดังนั้นก็ต้องประยุกต์เอาอีกแล้ว (ฮา)

//jQuery
container.on('click', '.box', function(event){
    //...
})

//Vanilla
container.addEventListener('click', function(event){
  if(event.target.matches('.box')){
      //...
  }
})

Create Event

อันนี้เป็นการสร้าง Event ขึ้นมาเอง .. ไม่ต่างกันมากนัก

//jQuery
ele.trigger('click')

//Vanilla
ele.dispatchEvent(new Event('click'))

Styling

การเซ็ต CSS ให้กับ Element

ของ jQuery จะใช้คำสั่ง .css() แต่วนิลาสามารถเซ็ตลงไปได้ผ่าน properties ที่ชื่อว่า .style

//jQuery
ele.css('color', '#000')
ele.css({
  'color': '#000',
  'background': '#FFF',
})

//Vanilla
ele.style.color = '#000'
ele.style.background = '#FFF'
ele.style.cssText = 'color: #000, background: #FFF'

บางครั้ง jQuery ก็มีฟังก์ชันที่จริงๆ เบื้องหลักก็ไปเซ็ตค่า css นั่นแหละแบบนี้ด้วยนะ

//jQuery
box.hide()
box.show()

//Vanilla
box.style.display = 'none'
box.style.display = 'block'

Document Ready

ในการรันสคริป บางทีเราต้องการจะให้หน้าเพจโหลดเสร็จซะก่อน สำหรับ jQuery นั้นเราใช้คำสั่ง ready() หรือส่งฟังก์ชันเข้าไปเลยก็ได้ โค้ดส่วนนี้จะทำงานเหมือน document ทั้งหมดโหลดเสร็จแล้ว

สำหรับ vanilla นั้นไม่มีคำสั่งสำเร็จขนาดนั้น แต่ถ้าอยากได้ก็เขียนได้ ... แต่เอาจริงๆ ส่วนใหญ่เราจะใช้วิธีเรียกฟังก์ชันที่ท้ายๆ ของหน้าเพจมากกว่า

//jQuery
$(document).ready(function(){ 
  /*...*/ 
})

$(function(){ 
  /*...*/ 
})

//Vanilla
(function(callback){
  if (document.readyState != "loading") callback()
  else document.addEventListener("DOMContentLoaded", callback)
})(function(){
   /*...*/
})

Class Properties

สำหรับสำหรับจัดการ class ซึ่งตอนนี้ฝั่ง vanilla ก็ทำได้ทั้งหมดเหมือน jQuery แล้วผ่าน properties ชื่อ classList

//jQuery
box.addClass('active focus')
box.removeClass('active')
box.toggleClass('active')
box.hasClass('active')

//Vanilla
box.classList.add('active', 'focus')
box.classList.remove('active')
box.classList.toggle('active')
box.classList.replace('active', 'blur')
box.classList.contains('active')

Create Virtual Element

Virtual Element เป็นการสร้าง Element HTML ขึ้นมาในฝั่ง JavaScript ซึ่งสามารถเอาไปปะให้แสดงผลใน document ได้ ซึ่งหลายๆ เฟรมเวิร์คตอนนี้ก็ใช้วิธีนี้แหละ ในการควบคุมการทำงานระหว่างสคริปกับ HTML

สำหรับ jQuery การสร้าง Virtual Element แค่เติม <> ครอบชื่อแท็กลงไปก็พอแล้ว ซึ่งเราจะได้ element ออกมา สามารถเอาไปใช้งานได้เลย

ทางฝั่ง vanilla ตอนนี้วิธีสร้าง Virtual Element ก็ไม่ยากล่ะ เพราะมีคำสั่ง createElement ให้ใช้

//jQuery
let div = $('<div>')
div.text('Hello World!')
div.html('Hello World!')

//Vanilla
let div = document.createElement('div')
div.textContent = 'Hello World!'
div.innerHTML = 'Hello World!'

DOM Manipulate

การจัดการ DOM อันนี้เราคิดว่าคำสั่งทางฝั่ง vanilla อ่านเข้าใจง่ายกว่า

คือแบ่งเป็น 2 เรื่อง การแก้ไข DOM ภายใน กับ ภายนอก (replace)

//jQuery
el.replaceWith('x')
el.html('x')

//Vanilla
el.outerHTML = 'x'
el.innserHTML = 'x'

ส่วนการเพิ่ม element เข้าไป ก็มี 2 คำสั่งคือเพิ่มต่อท้าย กับเพิ่มแทรกเข้าไปเป็นตัวแรก ซึ่งฝั่ง vanilla นั้นไม่มีคำสั่งเพิ่มแบบแทรกเข้าไปเป็นตัวแรก ต้องหา child ตัวแรกให้ได้ก่อน แล้วใช้ insertBefore ต่ออีกที

//jQuery
ul.append($('<li>'))
ul.prepend($('<li>'))

//Vanilla
ul.appendChild(document.createElement('li'))
ul.insertBefore(document.createElement('li'), parent.firstChild)

เรื่องการลบทิ้ง, การแทรกในตำแหน่งต่างๆ, และการ clone อันนี้ไม่ยากเท่าไหร่

//jQuery
el.remove()

//Vanilla
el.parentNode.removeChild(el)
//jQuery
$(target).after(element)
$(target).before(element)

//Vanilla
target.insertAdjacentElement('afterend', element)
target.insertAdjacentElement('beforebegin', element)
//jQuery
$(el).clone()

//Vanilla
el.cloneNode(true)

Attribute

เรื่องการจัดการ attribute ฝั่ง jQuery จะพิเศษหน่อยตรงมี data() ให้ใช้งานด้วย

//jQuery
el.prop('data-x')
el.attr('data-x')
el.data('x')

//Vanilla
el['data-x']
//jQuery
el.removeAttr('data-x')

//Vanilla
el.removeAttribute('data-x')

Effect

สำหรับ jQuery นั้นมีฟังก์ชันที่ทำให้เราโชว์/ซ่อน element แบบมีอนิเมชันได้ด้วย เช่น fadeOut, fadeIn, slideUp, slideDown (ซึ่งเบื้องหลังเป็นการสั่งเปลี่ยนค่าพวกความสูงหรือ opacity ด้วยลูปเอานะ)

สำหรับการเขียนแบบใหม่ ตอนนี้เราไม่นิยมให้ JavaScript เป็นคนจัดการอนิเมชันแล้ว แต่โยนไปเป็นหน้าที่ของ css แทน เช่นการใช้ transition

ฝั่ง JavaScript มีหน้าที่แค่ระบุคลาสของ css เข้าไปเท่านั้นพอ

//jQuery
$(el).fadeOut()

//Vanilla
el.classList.add('hide')
el.classList.remove('show')
/* With this CSS */
.show {
  opacity: 1;
}
.hide {
  opacity: 0;
  transition: opacity 400ms;
}

จริงๆ น่าจะยังมีอีกหลายคำสั่ง

9 Total Views 3 Views Today
Ta

Ta

สิ่งมีชีวิตตัวอ้วนๆ กลมๆ เคลื่อนที่ไปไหนโดยการกลิ้ง .. ถนัดการดำรงชีวิตโดยไม่โดนแสงแดด
ปัจจุบันเป็น Senior Software Engineer อยู่ที่ Centrillion Technology
งานอดิเรกคือ เขียนโปรแกรม อ่านหนังสือ เขียนบทความ วาดรูป และ เล่นแบดมินตัน

You may also like...