无法给动态创建的元素绑定事件,通过事件委托解决
瀑布流效果
<style type="text/css"></style>
<script>
let df = document.createDocumentFragment()
for (let i = 1; i < 43; i++) {
let oDiv = document.createElement("div")
oDiv.classList.add("item")
oDiv.innerHTML = `<img src='./images/img (${i}).jpg'/>`
df.appendChild(oDiv)
}
document.body.appendChild(df)
</script>
</head>
Uncaught TypeError: Cannot read properties of null (reading 'appendChild')
原因:
script在头部,html文件还未解析到body.所以目前无法获取到document.body(null),所以没办法在空属性上添加元素
造成下图效果的原因
let df = document.createDocumentFragment()
for (let i = 1; i < 43; i++) {
let oDiv = document.createElement("div")
oDiv.classList.add("item")
oDiv.innerHTML = `<img src='./images/img (${i}).jpg'/>`
df.appendChild(oDiv)
}
document.body.appendChild(df)
let oItems = document.querySelectorAll(".item")
let oArr = []
oItems.forEach((item,index)=>{
console.log(item.offsetHeight);
})
oItems.forEach((item, index) => {
if (index < 3) {
oArr[index] = item.offsetHeight
} else {
item.style.position = 'absolute'
let minIndex = getMinIndex(oArr)
item.style.left = minIndex * item.offsetWidth + 'px'
item.style.top = oArr[minIndex] + 'px'
oArr[minIndex] = oArr[minIndex] + item.offsetHeight
}
})
console.log(item.offsetHeight); -->31
为什么会是31?
div已经创建好了,但是里面的img还没有加载出来,所以这是div的自带样式高度,以及未加载完成的图片的高度,所以都是31px,所以导致这种局面
解决方案:
DOMContentLoaded 页面结构加载完成就触发
window.onload 页面结构加载完成,外部的src也要加载完成-
window.addEventListener("DOMContentLoaded", () => {
let df = document.createDocumentFragment()
for (let i = 1; i < 43; i++) {
let oDiv = document.createElement("div")
oDiv.classList.add("item")
oDiv.innerHTML = `<img src='./images/img (${i}).jpg'/>`
df.appendChild(oDiv)
}
document.body.appendChild(df)
})
window.onload = () => {
let oItems = document.querySelectorAll(".item")
let oArr = []
oItems.forEach((item,index)=>{
console.log(item.offsetHeight);
})
oItems.forEach((item, index) => {
if (index < 3) {
oArr[index] = item.offsetHeight
} else {
item.style.position = 'absolute'
let minIndex = getMinIndex(oArr)
item.style.left = minIndex * item.offsetWidth + 'px'
item.style.top = oArr[minIndex] + 'px'
oArr[minIndex] = oArr[minIndex] + item.offsetHeight
}
})
}
function getMinIndex(arr) {
return arr.findIndex(item => item == Math.min(...arr))
}
Ajax的同步与异步
如果是Ajax的同步,在点击按钮的时候,移动的盒子会停顿,直到服务器把数据传给浏览器,盒子才继续移动
使用利用Ajax的异步来解决这个问题
<button id="btn">click</button>
<div class="mes">xxxx</div>
<script>
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest()
}
return ActiveXObject("Msxml2.XMLTTP")
}
function ajax(url) {
// function ajax(url,callback) {
//得到xhr核心对象
let xhr=createXHR()
//准备请求参数
xhr.open('get',url)//第三个参数不写,默认为true 异步
//发送请求
xhr.send(null)
xhr.onreadystatechange=function(){
if (xhr.readyState==4&&xhr.status==200) {
return xhr.response
}
}
//设置监听的事件,所有的事件都是小写
}
//调用Ajax
document.querySelector('#btn').onclick=function(){
let res=ajax('http://useker.cn/getusers')
console.log(res);
}
let oMes=document.querySelector(".mes")
setInterval(()=>{
oMes.style.left=oMes.offsetLeft+10+'px'
if (oMes.offsetLeft>document.documentElement.clientWidth) {
oMes.style.left=0
}
},100)
</script>
为什么第28行console.log(res)打印的结果是undefined
首先在ajax方法中xhr.onreadystatechange=function(){}这个事件,只是将一个匿名函数function(){}赋给xhr的
onreadystatechange事件,内部代码在ajax这个方法中并不会执行,在第27行调用ajax方法,按理会把实参带入ajax方法中执行内部代码,但是onreadystatechange是一个事件,事件是异步的,只有当事件被触发的时候才会执行内部代码,我们不知道事件会在什么时候执行,所以ajax内部的同步代码顺序执行,跳过异步事件
xhr.onreadystatechange=function(){
if (xhr.readyState==4&&xhr.status==200) {
return xhr.response
}
}
到第24行结束ajax方法,执行第28行console.log(res);由于没有执行事件,在ajax方法中函数并没有return,所以打印的是undefined,而可能在打印之后在执行xhr的事件,获取到res的值
解决方法:
利用回调函数解决异步
回调函数会在事件执行完后返回数据
作用类似:A找B借钱,但是B此时没钱,过了一段事件B有钱了,再找B借钱就是另外一件事件了,如果A在B没钱的时候给B一张卡,让B有钱的时候就往卡里打钱,这样就还是一个事件,并且解决了时间的问题,所以回调函数就类似这里的银行卡
function ajax(url,callback) {
//得到xhr核心对象
let xhr=createXHR()
//准备请求参数
xhr.open('get',url)//第三个参数不写,默认为true 异步
//发送请求
xhr.send(null)
//设置监听的事件,所有的事件都是小写
xhr.onreadystatechange=function(){
//如如果状态发生变化,就在这里判断状态
if (xhr.readyState==4&&xhr.status==200) {
callback(xhr.response)
}
}
}
//调用Ajax
document.querySelector('#btn').onclick=function(){
ajax('http://useker.cn/getusers',function(res){
console.log(res);
})
}
闭包
//5个button
let oBtns=document.querySelectorAll("button")
for (var i = 0; i < oBtns.length; i++) {
oBtns[i].onclick=function(
{
console.log(i)
}
}
打印的i永远是5,因为点击事件是异步的,所以永远等待同步代码for循环执行结束后再执行,此时的i为5,
可以使用let/const关键字代替var关键字,或者提前将i的值存储起来,或者使用高阶函数,或者闭包
let oBtns=document.querySelectorAll("button")
for (let i = 0; i < oBtns.length; i++) {
oBtns[i].onclick=fn(i)
}
function fn(n) {
return function (e) {
for (let i = 0; i < oBtns.length; i++) {
oBtns[i].style.backgroundColor=''
}
oBtns[n].style.backgroundColor='green'
}
}
在oBtns[i].onclick=fn(i)中,fn(i)会立即执行,不受点击事件影响,放回第6行的函数,这个函数将在点击事件执行的时候触发执行,这个函数的事件e是点击事件,它的this指向事件触发对象OBox
节流
oBox.onmousemove=throttle(function (e) {
console.log('zhouhaha');
},2500)
function throttle(callback,delay=500) {
let startTime=Date.now()
return function (e) {
let currentTime=Date.now()
if (currentTime-startTime>=delay) {
startTime=currentTime
callback.bind(this)(e)
}
}
}
防抖
oBox.onmousemove=debounce(function(e){
console.log('zhouhaha');
},2500)
function debounce(callback,delay) {
let timer
return function(e){
let self=this
clearTimeout(timer)
timer=setTimeout(function () {
callback.bind(self)(e)
},delay)
}
}
动态属性
checked selected disabled ed结尾的都是动态属性
动态属性不能用attribute(attr)操作,要使用property(prop)操作
attr 如果未设置或者删除就无法获取
$(function () {
// $('button').eq(0).click(function () {
// $(":input").attr('checked',true)
// })
// $('button').eq(1).click(function () {
// console.log($('input:checkbox').attr('checked'));
// })
// $('button').eq(2).click(function () {
// $('input:checkbox').removeAttr('checked')
// })
// })
$(function () {
$('button').eq(0).click(function () {
$(":input").prop('checked',true)
})
$('button').eq(1).click(function () {
console.log($('input:checkbox').prop('checked'));
})
$('button').eq(2).click(function () {
$('input:checkbox').removeProp('checked')
})
})