jQuery 轉原生 JavaScript 對應語法
最近我嘗試將專案中的 jQuery 全部替換為原生 JavaScript,但發現這不是一個簡單的任務。除了更改語法外,許多依賴 jQuery 的套件也要尋找替代方案。 起初,我嘗試使用網路上的一些將 jQuery 轉換為 JavaScript 的工具,但這些工具大多存在一些缺陷,有些 jQuery 語法無法直接轉換,最後我還是必須手動一行一行檢查。
因此,最後我決定還是自己花時間慢慢重新調整程式碼,本文是我自己進行 jQuery 轉原生 JavaScript 語法時的筆記,希望能幫助到跟我一樣在進行換掉 jQuery 套件任務的開發人員。
相信換掉 jQuery 套件這項任務,應該很多開發人員都有遇到,不知道大家都是怎麼處理的呢?還是真的有工具可以處理,只是我沒找到而已呢?歡迎大家留言一起討論喔。
選擇元素
- 取得 body
-
$('body');document.body; - 基本選擇器
-
$('.class ul li a');document.querySelectorAll('.class ul li a'); - ID 選擇器
-
$('#id');document.querySelector('#id'); // 或 document.getElementById('id'); - Class 選擇器
-
$('.class');document.querySelectorAll('.class'); // 或 document.getElementsByClassName('class'); - 屬性選擇器
-
$('a[target=_blank]');document.querySelectorAll('a[target=_blank]'); - 尋找下一層
-
$item.find('li');item.querySelectorAll('li'); - 往上層尋找
-
// 只往上找一層 $item.parent('div'); // 往上找到符合條件的元素就停止 $item.closest('div'); // 往上找到所有符合條件的元素 $item.parents('div');// 只往上找一層,等於 $item.parent(); item.parentElement // 往上找到符合條件的元素就停止 item.closest('div'); // 往上找到所有符合條件的元素 // 原生沒有類似的寫法,需要自己寫 function 處理 - 兄弟選擇器
-
$('.find-siblings').siblings();let item = document.querySelector('.find-siblings'); let siblings = item.parentNode.querySelectorAll(':scope > :not(.find-siblings)'); // 或 let item = document.querySelector('.find-siblings'); let siblings = [...item.parentNode.children].filter((child) => child !== item ); - 上一個元素 / 下一個元素
-
// 上一個元素 $item.prev(); // 下一個元素 $item.next();// 上一個元素 item.previousElementSibling; // 下一個元素 item.nextElementSibling; item.previousElementSibling;
屬性
- 取得 / 設定 / 移除屬性
-
// 取得 foo 屬性值 $item.attr('foo'); // 設定 foo 屬性值為 bar $item.attr('foo', 'bar'); // 移除 foo 屬性 $item.removeAttr('foo');// 取得 foo 屬性值 item.getAttribute('foo'); // 設定 foo 屬性值為 bar item.setAttribute('foo', 'bar'); // 移除 foo 屬性 item.removeAttribute('foo'); - 取得 data-* 值
-
$item.data('foo');item.getAttribute('data-foo'); // 或 item.dataset['foo']; - 設定 data-* 值
-
$item.data('foo', 'bar');item.setAttribute('data-foo', 'bar'); // 或 item.dataset['foo'] = 'bar';以上寫法還是稍微與 jQuery 不同,用 jQuery.data() 設定資料時,並不會在 element 增加 data-* 的屬性,但用 jQuery.data() 抓資料卻抓得到資料。 另外,用 jQuery.data() 設定資料,是沒辦法用原生語法抓取資料。
// 用 jQuery 設定 data-foo 為 bar,並不會在 $item 增加 data-foo 的屬性 $item.data('foo', 'bar'); // 可以使用 jQuery.data() 抓資料 // 回傳 bar $item.data('foo') // 無法用原生語法抓資料 // 回傳 null item.getAttribute('data-foo'); // 回傳 undefined item.dataset['foo'];
樣式 (CSS 及 Style)
- 取得樣式
-
// jQuery.css() 會取得最終套用的樣式,而不是取得元素中的 style 屬性設定 $item.css('color');// 取得最終套用的樣式 item.ownerDocument.defaultView.getComputedStyle(item, null).color // 取得元素中的 style 屬性設定 item.style.color - 設定樣式
-
$item.css("color", '#ff0000'); // 或 $item.css({color: '#ff0000'});item.style.color = '#ff0000'; - add / remove / has / toggle Class
-
// add class $item.addClass(className); // remove class $item.removeClass(className); // has class $item.hasClass(className); // toggle class $item.toggleClass(className);// add class item.classList.add(className); // remove class item.classList.remove(className); // has class item.classList.contains(className); // toggle class item.classList.toggle(className); - Window height
-
$(window).height();// 不含 scrollbar,與 jQuery 一致 window.document.documentElement.clientHeight; // 含 scrollbar window.innerHeight; - Document height
-
$(document).height();const body = document.body; const html = document.documentElement; const height = Math.max( body.offsetHeight, body.scrollHeight, html.clientHeight, html.offsetHeight, html.scrollHeight ); - Element height
-
$item.height();function getHeight(item) { const styles = this.getComputedStyle(item); const height = item.offsetHeight; const borderTopWidth = parseFloat(styles.borderTopWidth); const borderBottomWidth = parseFloat(styles.borderBottomWidth); const paddingTop = parseFloat(styles.paddingTop); const paddingBottom = parseFloat(styles.paddingBottom); return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; } // 精確到整數 item.clientHeight; // 精確到小數 item.getBoundingClientRect().height; - Position
-
$item.position();{ left: item.offsetLeft, top: item.offsetTop } - Offset
-
$item.offset();function getOffset (item) { const box = item.getBoundingClientRect(); return { top: box.top + window.pageYOffset - document.documentElement.clientTop, left: box.left + window.pageXOffset - document.documentElement.clientLeft } } - ScrollTop
-
// 取得目前卷軸位置 $(window).scrollTop(); // 移動捲軸到指定位置 $(window).scrollTop(0);// 取得目前卷軸位置 window.scrollY; // 移動捲軸到指定位置 window.scrollTo({top: 0, behavior: 'smooth'});
DOM 操作
- get / set text
-
// Get text $item.text(); // Set text $item.text(string);// Get text item.textContent; // Set text item.textContent = string; - get / set html
-
// Get html $item.html(); // Set html $item.html(string);// Get html item.innerHTML; // Set html item.innerHTML = string; - 移除元素
-
$item.remove();item.remove(); - Prepend
-
將元素插入另一個元素的最前端。
$el.prepend('<div>hello</div>');// HTML string item.insertAdjacentHTML('afterbegin', '<div>hello</div>'); // Element item.insertBefore(newEle); - Append
-
將元素插入另一個元素的最末端。
$item.append('<div>hello</div>');// HTML string item.insertAdjacentHTML('beforeend', '<div>Hello</div>'); // Element item.appendChild(newEle); - insertBefore
-
將元素插入另一個元素之前。
$item.insertBefore(selector);// HTML string item.insertAdjacentHTML('beforebegin ', '<div>Hello</div>'); // Element const ele = document.querySelector(selector); if (ele.parentNode) { ele.parentNode.insertBefore(item, ele); } - insertAfter
-
將元素插入另一個元素之後。
$item.insertAfter(selector);// HTML string item.insertAdjacentHTML('afterend ', '<div>Hello</div>'); // Element const ele = document.querySelector(selector); if (ele.parentNode) { ele.parentNode.insertBefore(item, ele.nextSibling); } - is
-
$item.is(selector);item.matches(selector); - clone
-
$item.clone();item.cloneNode(); - wrap
-
$('.inner').wrap('<div class="wrapper"></div>');Array.from(document.querySelectorAll('.inner')).forEach((ele) => { const wrapper = document.createElement('div'); wrapper.className = 'wrapper'; ele.parentNode.insertBefore(wrapper, ele); ele.parentNode.removeChild(ele); wrapper.appendChild(ele); }); - unwrap
-
$('.inner').unwrap();Array.prototype.forEach.call(document.querySelectorAll('.inner'), (ele) => { let eleParentNode = ele.parentNode if(eleParentNode !== document.body) { eleParentNode.parentNode.insertBefore(ele, eleParentNode) eleParentNode.parentNode.removeChild(eleParentNode) } }); - 移除所有子結點 (empty)
-
$item.empty();item.innerHTML = ''; - 解析 HTML/SVG/XML 字串
-
$(` <ol> <li>a</li> <li>b</li> </ol> <ol> <li>c</li> <li>d</li> </ol> `);const range = document.createRange(); const parse = range.createContextualFragment.bind(range); parse(` <ol> <li>a</li> <li>b</li> </ol> <ol> <li>c</li> <li>d</li> </ol> `); // 單一元素可以使用以下方法 function htmlToDOM(html) { let div = document.createElement('div'); div.innerHTML = this.htmlMinify(html); return div.firstChild; } function htmlMinify(html) { return html ? html.replace(/\>[\r\n ]+\</g, "><").replace(/(<.*?>)|\s+/g, (m, $1) => $1 ? $1 : ' ').trim() : ''; } htmlToDOM(` <ol> <li>a</li> <li>b</li> </ol> `);
表單處理
- 取得 / 設定值
-
// 取得 ID 為 input 的值 $('#input').val(); // 設定 ID 為 input 的值為 name $('#input').val('name');// 取得 ID 為 input 的值 document.getElementById('input').value; // 設定 ID 為 input 的值為 name document.getElementById('input').value = 'name'; - 取得勾選的 checkbox 或 radio
-
$('.checkbox:checked');document.querySelectorAll('.checkbox:checked'); - 獲取焦點
-
$('#input').focus();document.getElementById('input').focus();
Iframe
- Iframe contents
-
$iframe.contents();iframe.contentDocument; - Iframe Query
-
$iframe.contents().find('.css');iframe.contentDocument.querySelectorAll('.css');
AJAX
直接改用 Fetch API,請參考以下網址:
事件
- document ready
-
$(document).ready(eventHandler);document.addEventListener('DOMContentLoaded', eventHandler); - 綁定 / 移除事件
-
// 綁定事件 $item.on(eventName, eventHandler); // 移除事件 $item.off(eventName, eventHandler);// 綁定事件 item.addEventListener(eventName, eventHandler); // 移除事件 item.removeEventListener(eventName, eventHandler); - trigger
-
$item.trigger('custom-event', {key1: 'data'});if (window.CustomEvent) { const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); } else { const event = document.createEvent('CustomEvent'); event.initCustomEvent('custom-event', true, true, {key1: 'data'}); } item.dispatchEvent(event);
實用工具
- isArray
-
檢查是否為陣列。
$.isArray(array);Array.isArray(array); - inArray
-
指定值,在陣列中的索引值。
// 傳回索引值,-1 表示 item 不在 array $.inArray(item, array);// 傳回索引值,-1 表示 item 不在 array array.indexOf(item); // 傳回 true 或 false array.includes(item); - isNumeric
-
檢查是否為數字。
$.isNumeric(item);function isNumeric(value) { return !isNaN(parseFloat(value)) && isFinite(value); } - isFunction
-
檢查是否為函數。
$.isFunction(item);function isFunction(item) { if (typeof item === 'function') { return true; } var type = Object.prototype.toString(item); return type === '[object Function]' || type === '[object GeneratorFunction]'; } - isEmptyObject
-
檢查是否為空。
$.isEmptyObject(obj);function isEmptyObject(obj) { return Object.keys(obj).length === 0; } - isPlainObject
-
檢查是否為扁平物件 (使用 {} 或 new Object 建立)。
$.isPlainObject(obj);function isPlainObject(obj) { if (typeof (obj) !== 'object' || obj.nodeType || obj !== null && obj !== undefined && obj === obj.window) { return false; } if (obj.constructor && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf')) { return false; } return true; } - extend
-
合併多個物件內容。
$.extend({}, defaultOpts, opts);Object.assign({}, defaultOpts, opts); - merge
-
合併兩個陣列內容。
const newArray = $.merge(array1, array2);// 不能去除重複 (與 jQuery 相同) const newArray = [...array1, ...array2]; // 不能去除重複 (與 jQuery 相同) function merge(...args) { return [].concat(...args) } const newArray = merge(array1, array2); // 可以去除重複 function merge(...args) { return Array.from(new Set([].concat(...args))) } const newArray = merge(array1, array2); - trim
-
移除字串頭尾空白。
$.trim(string);string.trim(); - each / map / grep
-
// each $.each(array, (index, value) => { }); // map $.map(array, (value, index) => { }); // grep $.grep(array, (value, index) => { });// each array.forEach((value, index) => { }); // map array.map((value, index) => { }); // grep array.filter((value, index) => { }); - now
-
取得當前時間。
$.now();Date.now(); - proxy
-
$.proxy(fn, context);fn.bind(context); - makeArray
-
將類似陣列的資料 (例如:HTMLCollection),轉換為真正的 JavaScript 陣列。
$.makeArray(arrayLike);Array.prototype.slice.call(arrayLike); // 或 Array.from(arrayLike); - contains
-
檢查 DOM 是否包含指定 DOM。
$.contains(item, child);item !== child && item.contains(child); - parseHTML
-
$.parseHTML(htmlString);function parseHTML(string) { const context = document.implementation.createHTMLDocument(); const base = context.createElement('base'); base.href = document.location.href; context.head.appendChild(base); context.body.innerHTML = string; return Array.from(context.body.children); } - parseJSON
-
$.parseJSON(str);// String to JSON JSON.parse(str); // JSON to String JSON.stringify(json);
動畫
- Show / Hide / Toggle
-
// Show $item.show(); // Hide $item.hide(); // Toggle $item.toggle();// Show item.style.display = 'block'; // Hide item.style.display = 'none'; // Toggle if (item.ownerDocument.defaultView.getComputedStyle(item, null).display === 'none') { item.style.display = 'block'; } else { item.style.display = 'none'; } - FadeIn & FadeOut
-
// FadeIn $item.fadeIn(3000); // FadeOut $item.fadeOut(3000); // FadeTo $item.fadeTo('slow', 0.15); // FadeToggle $item.fadeToggle();// FadeIn item.style.transition = 'opacity 3s'; item.style.opacity = '1'; // FadeOut item.style.transition = 'opacity 3s'; item.style.opacity = '0'; // FadeTo item.style.transition = 'opacity 3s'; item.style.opacity = '0.15'; // FadeToggle item.style.transition = 'opacity 3s'; const { opacity } = item.ownerDocument.defaultView.getComputedStyle(item, null); if (opacity === '1') { item.style.opacity = '0'; } else { item.style.opacity = '1'; } - SlideUp & SlideDown
-
// SlideUp $item.slideUp(); // SlideDown $item.slideDown(); // SlideToggle $item.slideToggle();// SlideUp const originHeight = '100px'; item.style.transition = 'height 3s'; item.style.height = '0px'; // SlideDown const originHeight = '100px'; item.style.transition = 'height 3s'; item.style.height = originHeight; // SlideToggle const originHeight = '100px'; item.style.transition = 'height 3s'; const { height } = item.ownerDocument.defaultView.getComputedStyle(item, null); if (parseInt(height, 10) === 0) { item.style.height = originHeight; } else { item.style.height = '0px'; } - Animate
-
$item.animate({ params }, speed);item.style.transition = 'all ' + speed; Object.keys(params).forEach((key) => { item.style[key] = params[key]; });
參考資料
本文絕大部分資料參考以下兩個網址,但有些語法我有經過修改。