JS必會基礎知識2

JS Web API

在如今各種框架的盛行的時代,讓開發(fā)人員手動操作DOM的機會越來越少,像Vue、React這樣的框架,內(nèi)部都封裝了各種DOM操作,但是DOM操作一直都會是前端工程師的基礎,也是必備的知識,只會Vue而不會DOM操作的前端工程師不會長久。

DOM(Document Object Model)是哪種數(shù)據(jù)結構(DOM的本質)

DOM是樹形結構,所以在操作DOM的時候才會有各種方法,比如獲取父節(jié)點、子節(jié)點、相鄰的節(jié)點

DOM操作常見的API

  • 節(jié)點操作
  • 結構操作
  • property操作
  • attribute操作

DOM節(jié)點操作

document.getElementById()
document.getElementsByClassName()
document.getElementByTagName()
document.getElementByName()

property操作

通過js屬性的形式操作樣式,修改dom元素對應的js屬性,不會提現(xiàn)到html結構中

div.style.width = '20px'

attribute操作

會修改html標簽上的屬性,會體現(xiàn)在html結構上

div.setAttribute('data-name', 'xxx');
console.log(div.getAttribute('data-name'));  // xxx

PS:通過property和attribute的方式,都會引起DOM渲染,但盡量使用property操作,attribute會改變頁面的html結構,所以attribute一定會使頁面寵幸渲染,而property可能會使頁面重新渲染

DOM結構操作

1. 新增節(jié)點

const div = document.createElement('div')

2. 插入節(jié)點

document.body.appendChild(div)

3. 刪除節(jié)點

document.body.removeChild(div)

4. 移動節(jié)點

// 未移動前的DOM結構
<div id="div1">
  <p id="p1">p1</p>
  <p>p2</p>
  <p>p3</p>
</div>
<div id="div2">
  <p>p4</p>
</div>

// 移動操作
<script>
  const p1 = document.getElementById('p1');
  const div2 = document.getElementById('div2');
  div2.appendChild(p1);  // 這里的p1是一個現(xiàn)有元素,故能夠移動
</script>

// 移動后的DOM結構
<div id="div1">
  <p>p2</p>
  <p>p3</p>
</div>
<div id="div2">
  <p>p4</p>
  <p id="p1">p1</p>
</div>

5.獲取子元素列表

div.childNodes

childNodes會獲取各種節(jié)點,如元素節(jié)點、注釋節(jié)點、文本節(jié)點。所以我們必須使用nodeName和nodeType屬性來判斷是不是你想要的元素

// 獲取所有的元素節(jié)點
nodeList.filter(child => child.nodeType === 1)

6. 獲取父元素

div.parentNode

DOM操作是十分昂貴的,所以應該避免頻繁的DOM操作,對DOM查詢進行緩存,將多次的操作轉化為一次性操作。

<div id="div1">
  <p>p1</p>
  <p>p2</p>
  <p>p3</p>
</div>

<script>
    // 對div1緩存
    const div1 = document.getElementById("div1");
    // 緩存div1的子元素
    const children = Array.prototype.slice
        .call(div1.childNodes)
        .filter(child => child.nodeType === 1);
    // 緩存長度
    const length = children.length;
    for (let i = 0; i < length; i++) {
        console.log(children[i].innerHTML);
    }
        // 將多次的操作轉化為一次性操作
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < length; i++) {
        const p = document.createElement("p");
        p.id = "p" + i;
        p.innerHTML = "this is p " + i;
        fragment.appendChild(p);
    }
        // 一次性去操作DOM
    div1.appendChild(fragment);
</script>

BOM(Browser Object Model)操作

1. navigator

// 獲取瀏覽器的信息
const ua = navigator.userAgent

2. screen

3. location

location.href // 全網(wǎng)址
location.protocal  // 協(xié)議
location.host  // 域名
location.search  // 查詢參數(shù)
location.hash  // 井號后面的內(nèi)容
location.pathname // 網(wǎng)站的路徑,包括井號

4. history

5. window

6. document

事件冒泡

順著DOM結構向上冒,事件源本身沒有事件

<div id="div1">
    <a href="#">a1</a>
    <a href="#">a2</a>
    <a href="#">a3</a>
</div>
<script>
    const div1 = document.getElementById("div1");
    div1.addEventListener("click", function(ev) {
        ev.preventDefault();  // 阻止默認行為,這里是a標簽,a標簽默認會跳轉,這里阻止a的默認跳轉行為
        console.log(ev.target);  // 點擊什么元素,它就是什么元素
    });
</script>

事件代理

事件代理是在事件冒泡的機制上去實行的。其優(yōu)點是代碼簡潔,減少瀏覽器的內(nèi)存占用,但不要濫用。使用場景,比如有一個很長的列表,這時候要判斷店家的是哪個item,這個時候就可以使用事件代理

<div id="div1">
    <a href="#">a1</a><br />
    <a href="#">a2</a><br />
    <a href="#">a3</a><br />
</div>
<button>add</button>
<script>
    const div1 = document.getElementById("div1");
    document.body.addEventListener("click", function(ev) {
        ev.preventDefault();
        const target = ev.target;
        const nodeName = target.nodeName;
        console.log(nodeName);
        if (nodeName === "A" || nodeName === "p") {
            console.log(target.innerHTML);
        } else if (nodeName === "BUTTON") {
            const fragment = document.createDocumentFragment();
            for (let i = 0; i < 5; i++) {
                const p = document.createElement("p");
                p.id = "p" + i;
                p.innerHTML = "this is p " + i;
                fragment.appendChild(p);
            }
            div1.appendChild(fragment);
        }
    });
</script>

編寫一個通用的事件監(jiān)聽函數(shù)

  <body>
    <div id="div1">
    <a href="#">a1</a><br />
    <a href="#">a2</a><br />
    <a href="#">a3</a><br />
    </div>
    <button>add</button>
  </body>

// 事件監(jiān)聽和事件代理
function bindEvent(el, type, fn, selector, prevent) {
    el.addEventListener(type, ev => {
        if (prevent) {
            ev.preventDefault();
        }
        const target = ev.target;
        const nodeName = target.nodeName;
        if (selector) {
            if (
                typeof selector === "string" &&
                selector.toUpperCase() === nodeName
            ) {
                // 將fn的this指向target
                fn.call(target, ev, target);
            }
        } else {
            fn.call(target, ev, target);
        }
    });
}
const div1 = document.getElementById("div1");
bindEvent(
    document.body,
    "click",
    function(ev) {
        console.log(this);
        console.log(this.innerHTML);
    },
    "a"
);

手寫一個簡易的ajax

function ajax({url, method, body, query, async = true}) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method.toUpperCase(), url, async) ;  // async表示是否異步,true為異步
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(xhr.responseText && JSON.parse(xhr.responseText));
        } else {
          reject(xhr)
        }
      }
    }
  xhr.send(body)
  })
}

xhr.readyState

  1. 未初始化,還未調用send()方法
  2. 載入完成,已調用send()方法,正在發(fā)送請求
  3. 交互,正在解析響應內(nèi)容
  4. 完成,響應內(nèi)容解析完成,可在客戶端調用

同源策略

ajax請求時,瀏覽器要求當前網(wǎng)頁和server必須同源(安全)
同源:協(xié)議、域名、端口,三者必須一致
加載圖片、css、js可無視同源策略

跨域

所有的跨域,都必須經(jīng)過server端的允許和配合
未經(jīng)server端允許實現(xiàn)跨域,說明瀏覽器有漏洞,危險信號

JSONP基本源流

script可繞過跨域限制
服務器可以任意動態(tài)拼接數(shù)據(jù)返回
所以,script就可以用來回去跨域的數(shù)據(jù),只要服務器愿意返回

window.cb = function(res) {
  console.log(res);  // 服務器返回的數(shù)據(jù)
}
<script src="xxx?user?callback=cb"></script>

// 服務器返回數(shù)據(jù)格式
cb({ name: "xxx" })
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容