函数顺序
submit >commentsObject>createEle>render 返回对象,=>bindEvent
1、提交事件
使用 submit()函数进行提交
绑定在提交按钮和键盘的ENTER键位上
1-1 submit做了什么
submit事件
- 判断文本是否为空,如果为空,则不进行后续操作
- 生成id,这个函数是生成的时间戳,具有唯一性
- 创建一个对象,commentsObject是一个构造函数,返回一个对象
- render进行渲染操作
- rander返回的对象,是我们需要操作的dom对象
- commentsArr对于数据层,做一个保存数据的数组
- bindEvent这个是对于需要操作的dom绑定事件
- domArr.push(domObj) 这个生成一个dom数组,这个是方便做全选操作
- clearHandler()清除添加完成后的副作用,比如文本清空
function submit (e) {
if (!commentStr.trim()) return
let id = new Date().getTime()
let item = commentsObject(commentStr, getData(), id)//生成一个对象,构造函数
let domObj = render(item, id)//渲染dom,返回一个对像,包含所有我们以后需要操作的dom
commentsArr.push(item);//评论添加到数据数组中,这个数组的作用 可以发给后端
bindEvent(domObj)//绑定事件
domArr.push(domObj)//添加一个数组,这个数组收集的是,我们所有的需要操作dom
clearHandler()//清除副作用,每一个我们完成操作后,需要将输入框数据清空,并且我们收集的commentStr也需要重置。
}
2-1commentsObject 构造函数
function commentsObject (text, time, id, name = "张三",) {
let obj = {};
obj.name = name;
obj.text = text;
obj.time = time;
obj.id = id
return obj;
}
name="张三"是一个默认值,他的作用,是在于每一次我们不传入name,自动生成
这个函数的本质,是一个构造函数,说通俗点,就是根据参数生成一个对象,免去每一次,我们都要重新的书写对象。减少重复书写对象的大量文本,有兴趣同学,可以看一下new 运算符
2-2 createEle生成dom的工具函数
他的作用:生成一个dom对象,并且返回一个它,他接受三个参数,都是string类型,
- tag 创建dom的标签
- className 创建dom的class类名
- text 创建dom的innerHTML文本
- ele是返回由参数创建的对象
function createEle (tag, className = "", text = "") {
let ele = document.createElement(tag);
ele.className = className;
if (text) ele.innerHTML = text;
return ele
}
2-3 render dom生成函数
arr数组,他的目的是在一个循环中,生成我们想要的dom对象
- forEach循环,每一次循环的过程中,会创建3个p标签的dom,本质上 相当于下面的不停的创建createDom,然后在appendChild到父元素中,循环的顺序,你可以用if判断,在中间插入dom,我们再这个text文本创建的后面,添加了input,最终决定顺序的是appendChild的插入顺序,我们这个是添加在text的dom之前的
- 如果理解有困难,则全部使用createDom和appendChild重构,这个commentDomObj对象,主要收集的是,我们传入数据和input文本修改框生成的dom
- 下文就是不停的创建dom,不停的插入dom。每一个dom的顺序和位置,是由appendChild的顺序决定的
function render (obj, id) {
//建议可以看一下Object.keys 和Object.value 然后尝试改写下面这个循环
let arr = [
"name", "text", "time"
] //3个对象的属性名称
let info = createEle("div", 'info');//创建info标签和类名
let commentDomObj = {}//收集我们创建评论的dom,放在一个对象中,方便进行管理
//循环创建重复dom,需要的展示文本
arr.forEach((item) => {
let ele = createEle("p", item, obj[item])//创建dom函数,返回值是当前创建的dom
if (item === "text") {
let input = createEle("input", "hidden");
info.appendChild(input)
commentDomObj["input"] = input
}
commentDomObj[item] = ele;
info.appendChild(ele);
})
let item = createEle("div", 'item');
let menu = createEle("div", "menu");
let leftTool = createEle("div", "leftTool");
let checkbox = createEle("input", "listCheckbox");//选择框
checkbox.type = "checkbox";//选择框
let del = createEle('div', "del", "删除");
//删除按钮
leftTool.appendChild(checkbox)
menu.appendChild(del)
item.appendChild(leftTool);
item.appendChild(info);
item.appendChild(menu);
document.querySelector(".list").appendChild(item)//将我们生成的dom添加到list类下面
return {
...commentDomObj, del: del, checkbox, id: id
}
}
3-1绑定事件
bindEvent(dom对象)专门用于绑定所有的事件
function bindEvent (domObj) {
//双击打开文本框事件
domObj.text.ondblclick = function (e) {
let textDom = domObj.text;
//input框
let inputDom = domObj.input;
//toggle,如果有class就删除,没有就添加
textDom.classList.toggle('hidden');
inputDom.classList.toggle("hidden");
//让input文本框的值,等于当前评论的文本
inputDom.value = textDom.innerHTML
//input显示出来后,自动聚焦
inputDom.focus();
}
//失去焦点保存事件
domObj.input.onblur = function (e) {
let textDom = domObj.text;
let inputDom = domObj.input;
let timeDom = domObj.time;
textDom.classList.toggle('hidden');
inputDom.classList.toggle("hidden");
let time = getData()//获取时间
//修改收集数据数组的值
let index = commentsArr.findIndex(item => item.id === domObj.id);
commentsArr[index].text = inputDom.value
commentsArr[index].time = time;
// 修改当前innerHTML文本
textDom.innerHTML = inputDom.value;
timeDom.innerHTML = time;
}
//删除dom事件
domObj.del.onclick = function (e) {
let index = commentsArr.findIndex(item => item.id === domObj.id);
let domIndex = domArr.findIndex(item => item.id === domObj.id);
commentsArr.splice(index, 1);
domArr.splice(index, 1);
e.target.parentElement.parentElement.remove()
}
domObj.checkbox.onchange = function (e) {
let checkLength = 0
for (let i = 0; i < domArr.length; i++) {
const ele = domArr[i];
if (ele.checkbox.checked) {
checkLength++
}
}
if (checkLength === domArr.length) {
allCheckDom.checked = true
} else {
allCheckDom.checked = false
}
}
}
3-2双击出现和隐藏修改文本框
ondblclick是鼠标左键双击事件
- 我们通过出传入的dom对象,获取到text文本dom和input输入框dom
- 使用classList.toggle()api实现对隐藏css类的添加,来决定dom是否显示
- 显示之后,我们需要使input需要有原来的值,我们使用value属性来设置
- 使用focus()api事件,使input出现后,焦点自动聚焦到输入框
domObj.text.ondblclick = function (e) {
let textDom = domObj.text;
//input框
let inputDom = domObj.input;
//toggle,如果有class就删除,没有就添加
textDom.classList.toggle('hidden');
inputDom.classList.toggle("hidden");
//让input文本框的值,等于当前评论的文本
inputDom.value = textDom.innerHTML
//input显示出来后,自动聚焦
inputDom.focus();
}
3-3失去焦点后的事件
失去焦点事件,更新事件,切换显示的dom,更新文本,更新收集数据的数组
- 我们获取相应的dom,然后同toggle对于有hidden的class类名进行删除,没有的则添加
- 使用getDate()更新时间
- findIndex接受一个回调,通过每个对象的id是否等于当前dom对象的id ,索引到需要更改的数据对象
- 让收集的数组更新文本,更新时间
- 页面使用innerHTML进行渲染
domObj.input.onblur = function (e) {
let textDom = domObj.text;
let inputDom = domObj.input;
let timeDom = domObj.time;
textDom.classList.toggle('hidden');
inputDom.classList.toggle("hidden");
let time = getData()//获取时间
//修改收集数据数组的值
let index = commentsArr.findIndex(item => item.id === domObj.id);
commentsArr[index].text = inputDom.value
commentsArr[index].time = time;
// 修改当前innerHTML文本
textDom.innerHTML = inputDom.value;
timeDom.innerHTML = time;
}
3-4删除dom事件
通过id找到对应的收集数据的数组和dom对象的数组
- 找到收集数据数组中的对应数据对象
- 使用splice将其从数组中移除
- 通过e.terget找到父元素的父元素,目的在于找到整个评论对象的dom可以通过打印来看 获取的是什么
- 通过removeapi移除
domObj.del.onclick = function (e) {
let index = commentsArr.findIndex(item => item.id === domObj.id);
let domIndex = domArr.findIndex(item => item.id === domObj.id);
commentsArr.splice(index, 1);
domArr.splice(index, 1);
e.target.parentElement.parentElement.remove()
}
3-4全选事件
通过计数的手段,判断选择中的checked值的多少,是否和全部dom数组的长度相等,如果相等,则将全选框dom的checked状态改为true,如果不相等,则将全选框的状态变更为false
- 设置一个0的数字,对数组的长度进行循环
- domArr里面所存放的dom,是会跟着真实的dom发生变化的,它实际上就是页面上存在的dom元素
- 判断是否是选中状态,如果是则++
- 将最终长度和所获取checked的dom的元素数量进行对比,如果相等 则全选
外部的全选框事件
- 我们在render生成dom的时候,就已经将dom数组收集完毕了
- 我们再点击全选框后,会使它的checked状态发生变化,我们使用onchange事件去监听它
- 如果发生的变化,则将所有dom数组中的checkbox都设置为当前全选框的checked值
//每个dom的选择框事件
domObj.checkbox.onchange = function (e) {
let checkLength = 0
for (let i = 0; i < domArr.length; i++) {
const ele = domArr[i];
if (ele.checkbox.checked) {
checkLength++
}
}
//或者checkLength >= domArr.length
if (checkLength === domArr.length) {
allCheckDom.checked = true
} else {
allCheckDom.checked = false
}
}
//外部全选框事件
allCheckDom.onchange = function (e) {
for (let i = 0; i < domArr.length; i++) {
domArr[i].checkbox.checked = e.target.checked;
}
}