最近我嘗試將專案中的 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];
                                });
                            
                        

參考資料

本文絕大部分資料參考以下兩個網址,但有些語法我有經過修改。