JS 基础(四):DOM、事件、ajax

本文深入探讨了DOM(文档对象模型)的property与attribute的区别,包括节点操作如新建、插入、移动和删除,以及DOM性能优化策略。接着,详细介绍了事件处理,包括事件监听函数的编写和事件冒泡的流程。此外,文章还讲解了AJAX的基本实现,xhr.readyState和xhr.status的状态解析,以及跨域的多种实现方式。通过实例和练习题,帮助读者巩固和应用所学知识。
摘要由CSDN通过智能技术生成

一、DOM

1.1 property与attribute的区别

前者修改js变量的值(可修改对象属性),但不会作用到html结构中;
后者直接修改html标签属性,会改变html结构;
两者都可能引起DOM渲染
property:

var p1 = document.querySelectorAll('p')[0]
p1.style.width = '100px'

attribute:

var p1 = document.querySelectorAll('p')[0]
p1.setAttribute('data-name', 'imooc')

1.2 节点操作

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>dom 演示</title>

        <style>
            .container {
                border: 1px solid #ccc;
            }
            .red {
                color: red;
            }
        </style>
    </head>
    <body>
        <div id="div1" class="container">
            <p id="p1">一段文字 1</p>
            <p>一段文字 2</p>
            <p>一段文字 3</p>
        </div>
        <div id="div2">
            <img src="https://img3.mukewang.com/5a9fc8070001a82402060220-100-100.jpg"/>
        </div>
        <ul id="list">
        </ul>

        <script src="./dom-3.js"></script>
    </body>
</html>
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')

1.新建节点:document.createElement(tagname)

const newP = document.createElement('p')
newP .innerHTML = 'this is newP'

2.插入节点:父元素.appendChild(新标签)

div1.appendChild(newP)

3.移动节点:其他父元素.appendChild(其他父元素或其子元素)

const p1 = document.getElementById('p1')
div2.appendChild(p1)

4.获取父元素:子元素.parentNode
5.获取子元素列表:父元素.childNodes(包含子元素的纯文本
解决方案:const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => { if (child.nodeType === 1) { return true } return false })

6.删除子元素

div1.removeChild( div1ChildNodesP[0] )

1.3 DOM性能优化

避免频繁的dom操作:

  1. for循环操作元素列表时,用js变量length存储document.getElementByClassName().length的值
for(let i=0;i<document.getElementByClassName().length;i++){
	...
} 

改为:

var length = document.getElementByClassName().length
for(let i=0;i<length ;i++){
	...
} 
  1. 对于for循环内部频繁的dom操作(比如一次性插入多个节点),还可以先建立一个文档片段,最后统一插到DOM结构中
const list = document.getElementById('list')
for (let i  = 0; i < 20; i++) {
    const li = document.createElement('li')
    li.innerHTML = `List item ${i}`
    list.appendChild(li)               			//频繁dom操作                 
} 
console.log(list)

改为

const list = document.getElementById('list')

// 创建一个文档片段,此时还没有插入到 DOM 结构中
const frag = document.createDocumentFragment()

for (let i  = 0; i < 20; i++) {
    const li = document.createElement('li')
    li.innerHTML = `List item ${i}`

    // 先插入文档片段中
    frag.appendChild(li)                       //避免频繁dom操作
} 

// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag)

console.log(list)

二、事件

2.1 编写一个通用的事件监听函数

事件绑定:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>事件 演示</title>
        <style>
            div {
                border: 1px solid #ccc;
                margin: 10px 0;
                padding: 0 10px;
            }
        </style>
    </head>
    <body>
        <button id="btn1">一个按钮</button>

        <div id="div1">
            <p id="p1">激活</p>
            <p id="p2">取消</p>
            <p id="p3">取消</p>
            <p id="p4">取消</p>
        </div>
        <div id="div2">
            <p id="p5">取消</p>
            <p id="p6">取消</p>
        </div> 

        <div id="div3">
            <a href="#">a1</a><br>
            <a href="#">a2</a><br>
            <a href="#">a3</a><br>
            <a href="#">a4</a><br>
            <button>加载更多...</button>
        </div>

        <script src="./event.js"></script>
    </body>
</html>
// 普通绑定的事件监听函数
function bindEvent(elem, type, fn) {
    elem.addEventListener(type, fn)
}
// 普通绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', function (event) {
    // console.log(event.target) // 获取触发的元素
    event.preventDefault() // 阻止默认行为
    alert(this.innerHTML)
})

// 包含代理绑定和普通绑定的通用事件监听函数
function bindEvent(elem, type, selector, fn) {
    if (fn == null) {
        fn = selector
        selector = null
    }
    elem.addEventListener(type, event => {
        const target = event.target
        if (selector) {
            // 代理绑定
            if (target.matches(selector)) {
                fn.call(target, event)
            }
        } else {
            // 普通绑定
            fn.call(target, event)
        }
    })
}

// 代理绑定
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function (event) {
    event.preventDefault()
    alert(this.innerHTML)
})

2.2 描述事件冒泡的流程

事件冒泡:基于DOM树形结构,事件会顺着触发元素依次往靠近根节点方向冒泡。

const body = document.body
bindEvent(body, 'click', event => {
    console.log('body clicked')
    // console.log(event.target)
})

// 普通绑定
const div2 = document.getElementById('div2')
bindEvent(div2, 'click', event => {
	event.preventDefault() // 阻止默认行为
	event.stopPropagation()// 阻止body冒泡
    console.log('div2 clicked')
    console.log(event.target)// 获取触发的元素
})

const p5 = document.getElementById('p5')
bindEvent(p5, 'click', event => {
    event.stopPropagation() // 阻止div2和body冒泡
    console.log('p5 clicked')
})

练习题: 无限下拉的图片列表,如何监听每个图片的点击

提示:事件代理;用event.target获取触发元素;用matches判断是否为触发元素。

三、ajax

3.1 ajax实现流程

  1. post请求
const xhr = new XMLHttpRequest()
xhr.open('POST',url,true)
xhr.onreadystatechange = function(){
	if (xhr.readyState===4 && xhr.status === 200) 
		//JSON.parse(xhr.responseText)
		console.log(xhr.responseText)
	else alert('error')
}
postData = {}
xhr.send(JSON.stringify(postData))
  1. get请求(结合promise)
function ajax(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open('GET', url, true)
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(
                        JSON.parse(xhr.responseText)
                    )
                } else if (xhr.status === 404 || xhr.status === 500) {
                    reject(new Error('404 not found'))
                }
            }
        }
        xhr.send(null)
    })
    return p
}

const url = '/data/test.json'
ajax(url)
.then(res => console.log(res))
.catch(err => console.error(err))

3.2 xhr.readyState 和 xhr.status

  1. xhr.readyState
 0: 未初始化,还未调用open方法
 1:(载入)已调用send方法,正在发送请求
 2:(载入完成)已接收到全部响应内容
 3:(交互)正在解析响应内容
 4:(完成)响应内容解析完成,可在客户端调用
  1. xhr.status
1xx:服务器收到请求
2xx:成功处理请求,200
3xx:重定向,浏览器直接跳转,301永久重定向,302临时重定向,304资源未改变
4xx:客户端请求错误,404请求路由未找到,403权限不够
5xx:服务端错误

3.3 跨域的实现方式

  1. 跨域与同源
    ajax请求时,浏览器要求客户端和服务端必须同源(协议、域名、端口三者必须一致),所有的跨域都必须经过server端允许和配合

  2. 图片标签src属性、link标签的href属性(css)、script标签的src属性(js)可无视同源策略

  3. jsonp
    jsonp的实现原理:script标签可绕过跨域限制,获得跨域数据;服务器可任意拼接数据返回

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>jsonp 演示</title>
    </head>
    <body>
        <p>一段文字 1</p>

        <script>
            window.abc = function (data) {
                console.log(data)
            }
        </script>
        <script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
    </body>
</html>

jsonp.js

abc(
    { name: 'xxx' }
)

jQuery实现jsonp

$.ajax({
	url:"http://localhost:8002/jsonp.js?username=xxx&callback=abc",
	dataType:'jsonp',
	jsonpCallback:'abc',
	success:function (data){
		console.log(data)
	}
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值