後jQuery時代

shellteo發表於2017-09-26

(轉自公眾號:前端八點半,已授權)

後jQuery時代

前言

  在MV*框架興起,前端告別刀耕火種進入工程化的今天,還沒來得及學習Angular,Facebook就帶著react框架降臨,不久vue.js又在這些框架打得火熱的時候脫穎而出,各種框架充斥著我們的視覺神經,好像操作DOM來寫前端的思想已經退出了歷史舞臺。而jQuery作為那個年代前端er主要的庫,對於DOM的增刪改查的便利讓我們對jQuery頂禮膜拜,jQuery為了簡化js而生,卻又因簡化js而死,jQuery釋出第一版距今已經11年之久,而後jQuery時代,我們需要怎麼做?

  第一版jQuery誕生於2006年,當初jQuery的誕生有兩個目的,第一簡化DOM操作,第二減少開發過程中跨瀏覽器的問題。

  他的創始人John Resig給這些程式碼新增了註釋,如下有第一版jQuery的地址,你可以第一版的原始碼。genius.it/johnresig.c…

  jQuery是偉大的,但是在當下技術發展的今天,我們不需要再相容IE6和IE7的情況下,很多相容性問題瀏覽器已經自行解決了,所以說,將jQuery從你的網站中刪除是正確的做法。而且js現在已經更加簡潔,對於很多DOM操作,用原生js來寫也會非常簡潔。

jQuery速覽

1.文件載入後執行:

$(document).ready(function(){})
$(function(){})
複製程式碼

2.jQuery事件繫結
onbindlivedelegate

3.jQuery增刪改查

增加:
append() - 在被選元素的結尾插入內容
prepend() - 在被選元素的開頭插入內容
after() - 在被選元素之後插入內容
appendTo() - 把所有匹配的元素追加到指定的元素元素集合中。

刪除:
remove() - 刪除被選元素(及其子元素)

$("p").remove(".italic");
複製程式碼

empty() - 從被選元素中刪除子元素

$("#div1").empty();
複製程式碼

修改:
設定內容- text()html() 以及 val()
text() - 設定或返回所選元素的文字內容

$("#test1").text("Hello world!");
複製程式碼

html() - 設定或返回所選元素的內容(包括 HTML 標記)

$("#test2").html("Hello world!");
複製程式碼

val() - 設定或返回表單欄位的值

$("#test3").val("Dolly Duck");
複製程式碼

設定屬性- attr()
jQuery attr() 方法也用於設定/改變屬性值。
attr設定多個值

$("#w3s").attr({
    "href" : "http://www.w3school.com.cn/jquery",
    "title" : "W3School jQuery Tutorial"
});
複製程式碼

獲取:
獲得內容- text()html() 以及 val()粗體文字
text() - 設定或返回所選元素的文字內容
html() - 設定或返回所選元素的內容(包括 HTML 標記)
val() - 設定或返回表單欄位的值

獲取屬性- attr()
jQuery attr() 方法用於獲取屬性值

$("#w3s").attr("href")
複製程式碼

查詢:
1.基本過濾器:
a) :first,選取第一個元素,別忘記它也是被放在一個集合裡哦!因為JQuery它是DOM物件的一個集合。如,“$(“tr:first”)”返回所有tr元素的第一個tr元素,它仍然被儲存在集合中。

b) :last,選取最後一個元素。如:“$(“tr:last”)”返回所有tr元素的最後一個tr元素,它仍然被儲存在集合中。

c):even:odd奇偶過濾器

d):not選擇所有不符合的元素

e):eq選擇索引匹配的元素

2.子元素過濾器
:first-child:last-child)選擇所有父級元素下的第一個(最後一個)子元素。
:first-of-type:last-of-type)選擇所有相同的元素名稱的第一個(最後一個)兄弟元素
:nth-child(n) 選擇的他們所有父元素的第n個子元素。
:nth-last-child(n)選擇所有他們父元素的第n個子元素。計數從最後一個元素開始到第一個。
:nth-of-type(n) 選擇同屬於一個父元素之下,並且標籤名相同的子元素中的第n個。
find(selector) 查詢該節點所有的子孫節點
children(selector)查詢所有的子節點,不過該方法只會返回直接的子節點,不會返回所有的子孫節點

3.父元素過濾器
parent(selector) 查詢父元素,可傳入selector進行過濾(下同)
parents(selector)查詢所有的祖先節點

4.兄弟元素過濾器
siblings() 查詢該節點所有的兄弟節點,不分前後
prev()查詢該節點的上一個兄弟節點
prevAll()查詢該節點之前所有的節點
next()查詢該節點的下一個兄弟節點
nextAll()查詢該節點之後所有的節點

去除DOM依賴

如何在專案中去除jQuery依賴,以及如何使用原生js替代jQuery中方法?
1.查詢DOM
getElementById():通過id查詢元素,訪問DOM最快的方法,而且大部分瀏覽器都支援
getElementsByClassName():通過class查詢元素
注:上面兩個方法很快,但由於僅通過類名選擇元素的限制,作用有限。

//polyfill,實現getElementsByClassName
function getElementsByClassName(className) {
    var all = document.all ? document.all : document.getElementsByTagName('*');
    var elements = new Array();
    for (var e = 0; e < all.length; e++) {
        if (all[e].className == className) {
            elements[elements.length] = all[e];
            break;
        }
    }
    return elements;
}
複製程式碼

querySelector()querySelectorAll():匹配指定 CSS 選擇器元素的第一個子元素(全部元素)。
注:querySelectorAll獲取到的是一個類陣列,可以通過Array.prototype.slice.call()將類陣列轉為陣列。

// jquery選擇器部分實現,container引數可選,獲取全部符合的元素
function $(selector, container) {
  return (container || document).querySelectorAll(selector);
}
// 獲取一個符合的元素
function $one(selector, container) {
  return (container || document).querySelector(selector);
}
複製程式碼

getElementsByTagName:通過標籤名查詢元素,跨瀏覽器安全快速的方法,獲取到一個類陣列。

2.查詢遍歷
獲取父類元素-parentNode
獲取子類元素-childNodefirstChildlastChild
獲取同級元素-nextSiblingpreviousSibling,只獲取一個匹配的

//獲取全部的同級元素
function getSiblings(el, filter) {
    var siblings = [];
    el = el.parentNode.firstChild;
    do { if (!filter || filter(el)) siblings.push(el); } while (el = el.nextSibling);
    return siblings;
}
複製程式碼

匹配特定選擇器且離當前元素最近的祖先元素-closest()[實驗中的功能,有的瀏覽器不相容]

// polyfill,不相容瀏覽器的實現方法
this.Element && function(ElementPrototype) {
    ElementPrototype.closest = ElementPrototype.closest ||
        function(selector) {
            var el = this;
            while (el.matches && !el.matches(selector)) el = el.parentNode;
            return el.matches ? el : null;
        }
}(Element.prototype);
複製程式碼

matches():如果元素將被指定的選擇器字串選擇,Element.matches() 方法返回true; 否則返回false

//polyfill
if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;           
        };
}
複製程式碼

3.DOM操作
建立DOM-createElement
替換DOM-replaceChild
刪除DOM(從DOM中刪除元素的父元素,使元素的內容保持原樣)

/*
 *刪除element,但是保留element裡面的子元素的做法
 */
var element = document.querySelector('.container');
// 獲取元素的父元素
var parent = element.parentNode;
// 遍歷將所有子元素從這個元素中移出去
while (element.firstChild) parent.insertBefore(element.firstChild, element);
// 刪除這個元素
parent.removeChild(element);
清空元素的內容-DOM中刪除元素的所有子節點。
var el = document.querySelector('div');
//通過設定innerHTML為空清空所有子節點
el.innerHTML = '';
刪除DOM-removeChild方法從DOM中刪除一個子節點。返回刪除的節點。
//從parent中刪除child,函式的返回值也是child的引用
parent.removeChild(child);
插入DOM-insertBefore、appendChild
//insertBefore,在parent節點中將child節點插入到node1之前
parent.insertBefore(child, node1);
//appendChild,將child節點插入到parent的最尾部
parent.appendChild(child)
複製程式碼

獲取元素的text內容-textContentinnerText(IE8)

let node = document.querySelector('container');
let text = node.textContent || node.innerText;
複製程式碼

獲取/設定元素的HTML內容-innerHTML屬性
複製當前節點(或者複製當前節點以及它的所有子孫節點)-cloneNode()

4.屬性操作-用於獲取和設定元素的DOM屬性的功能

設定、獲取和刪除DOM元素屬性
原生JavaScript可一直接訪問元素的屬性,例如:hreftitlealtvalue,刪除這些屬性的可以使用delete關鍵字

var node = document.querySelector('selector');
//設定屬性property
node.property = ''
//使用delete刪除property屬性
delete node.property;
新增,刪除和測試class
//判斷node是否有className
function hasClass(node, className) {
    return node.classList ?
        node.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(node.className);
}
//新增class
function addClass(node, className) {
    if (node.classList) {
        node.classList.add(className);
    }
    else if (!hasClass(node, className)) {
        node.className += ' ' + className;
    }
}
//移除class
function removeClass(node, className) {
    if (node.classList) {
        node.classList.remove(className);
    }
    else {
        node.className = node.className.replace(new RegExp('\\b' + className + '\\b', 'g'), '');
    }
}
複製程式碼

獲取,設定和刪除屬性-getAttributesetAttributeremoveAttribute

let node = document.querySelector('selector');
//在node上設定myProp屬性值為mydata
node.setAttribute('myProp', 'mydata');
//獲取myProp屬性
console.log(node.getAttribute('myProp'));
//刪除myProp屬性
node.removeAttribute('myProp');
複製程式碼

5.獲取設定元素樣式

樣式獲取-getComputedStyle(IE9以下:currentStyle

var node = document.querySelector('selector');
// IE上使用currentStyle
var style = window.getComputedStyle ? getComputedStyle(node, null) : node.currentStyle;
複製程式碼

樣式設定- stylecssText

function setCss(node, styles) {
    for (var property in styles){
        node.style[property] = styles[property];
    }
}
//使用cssText能夠同時設定多個樣式
node.style.cssText += 'color:red;'
複製程式碼

各種位置獲取如下:
獲取並設定元素的滾動位置-scrollTopscrollLeft
獲取元素相對於其父元素的偏移位置-offsetLeftoffsetTop
獲取相對於文件的元素的位置-getBoundingClientRect

function offset(node) {
  let rect = node.getBoundingClientRect(),
  scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
  scrollTop = window.pageYOffset || document.documentElement.scrollTop;
  return { top: rect.top + scrollTop, left: rect.left + scrollLeft }
}
複製程式碼

獲取元素的寬度和高度-根據盒模型,元素包含marginborderpaddingcontentwidth+height
獲取content+padding+borderoffsetWidth/offsetHeight
獲取content+padding:clientWidth/clientHeight
獲取marginborderpadding的值可以通過getComputedStyle
獲取視窗的寬高:window.innerWidthwindow.innerHeight
獲取文件的寬高:document.documentElement.clientWidth/Height

6.原生ajax
get請求和post請求

function getRequest(url, success) {
    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    xhr.open('GET', url);
    xhr.onreadystatechange = function () {
        if (xhr.readyState > 3 && xhr.status == 200) success(xhr.responseText);
    };
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.send();
    return xhr;
}
function postRequest(url, data, success) {
    var params = typeof data == 'string' ? data : Object.keys(data).map(
        function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }
    ).join('&');
    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
    xhr.open('POST', url);
    xhr.onreadystatechange = function() {
        if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); }
    };
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send(params);
    return xhr;
}
複製程式碼

非同步載入js,async或者defer關鍵字也可以非同步

//1.通過動態插入script標籤非同步載入
let scriptNode = document.createElement('script'),
  htmlNode = document.getElementsByTagName('script')[0];
scriptNode.src = url;
scriptNode.parentNode.insertBefore(scriptNode, htmlNode);
//2.通過async和defer屬性來非同步載入
<script src="" async defer></script>
複製程式碼

7.原生事件
阻止冒泡:

window.event? window.event.cancelBubble = true : e.stopPropagation();
複製程式碼

阻止預設事件:

window.event? window.event.returnValue = false : e.preventDefault();
複製程式碼

獲取鍵盤點選:

var key = window.event ? event.keyCode : event.which;
console.log(key);
複製程式碼

獲取滑鼠點選位置:

function handler(event) {
    event = event || window.event;
    let pageX = event.pageX;
    let pageY = event.pageY;
    // IE 8
    if (pageX === undefined) {
        pageX = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
        pageY = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
    return {
        pageX:pageX,
        pageY:pageY
    }
}
複製程式碼

事件繫結和解綁:

//新增事件
function addEvent(node, type, handler) {
    if (node.attachEvent) {
        node.attachEvent('on'+type, handler);
    } else {
        node.addEventListener(type, handler);
    }
}
//解除繫結
function removeEvent(node, type, handler) {
    if (node.detachEvent) {
        node.detachEvent('on'+type, handler);
    } else {
        node.removeEventListener(type, handler);
    }
}
複製程式碼

文件載入事件:

//對應於$(document).ready()的原生實現
document.addEventListener('DOMContentLoaded', function(){});
複製程式碼

(end)

相關文章