前言
作为前端开发,我们最应该重视的就是用户的体验,要如何将一个EditInPlace做到满分呢?今天我们就来拿下这道大厂面题,剑指B站。
Form表单实现
首先我们来看看最传统的修改数据提交表单:
<body>
<form action="https://www.baidu.com" method="post">
<div>
<!-- 点击label时,浏览器会自动将焦点设置到对应的输入框上 -->
<label for="input">签名</label>
<input type="text"
name="signature"
id="input"
placeholder="请输入签名"
required
>
</div>
<div>
<input type="submit" value="提交">
</form>
</body>
</html>
这是页面呈现的效果:
这边由于我们只看前端部分,所以我们用post方法将数据上传到百度上,目的只是为了看它的弹窗,在你输入文字点击提交后,页面会自动跳转到百度界面。
这就是最传统的提交表单添加数据并刷新页面,它的流程就是类似于你在网站上输入你的账号密码然后点击登陆就会跳转进入网站主页。但是这种形式对于用户来说体验肯定不如就地编辑,要是只到这种程度可能就直接被挂了。
Dom编程实现就地编辑
要实现这个任务,我们先来梳理一下它的框架,既然是就地编辑,那么肯定要分成两种模式,一种为显示在页面上的文本模式,一种是可以编辑签名的编辑模式,有了这个想法我们就可以初步创建我们的一些标签:
<div id="app">
<div id="ep1">
<span id="content">我是奶龙</span>
<input type="text" id="input" value="我是奶龙">
<input type="button" id="save" value="Save">
<input type="button" id="cancel" value="Cancel">
</div>
</div>
用于展示内容的content标签,修改签名的输入框input以及保存和取消按钮:
然后我们需要通过dom编程在script来获取标签中的元素用来实现html的动态操作:
// dom js html 节点对象 动态操作html
const content = document.getElementById('content');
const input = document.getElementById('input');
const save = document.getElementById('save');
const cancel = document.getElementById('cancel');
document.getElementById 是 JavaScript 中用于根据元素的 id 属性获取单个 DOM 元素的方法。它是 Document 接口的一部分,允许开发者通过唯一的 id 来快速定位并操作页面中的特定元素。
然后我们需要将文本模式和编辑模式分开来,于是需要在文本模式隐藏输入框以及按钮;在编辑模式隐藏内容,所以我们定义两个函数将这两种状态封装起来:
// 文本状态
function convertToText(){
input.style.display = 'none';
save.style.display = 'none';
cancel.style.display = 'none';
content.style.display = 'inline';
}
// 编辑状态
function convertToEdit(){
input.style.display = 'inline';
save.style.display = 'inline';
cancel.style.display = 'inline';
content.style.display = 'none';
}
convertToText();
利用display = 'none'将标签隐藏起来脱离文档流和display = 'inline'将标签设置为行内元素排列,最后调用文本状态函数,页面上就只剩下内容了:
实现就地编辑的关键步骤来了,我们需要如何进入编辑模式呢?我们可以在内容content上面添加一个监听点击事件,利用addEventListener方法,当鼠标点击内容时,我们就可以就地进入编辑状态:
// 点击文本 进入编辑状态
content.addEventListener('click',function(){
convertToEdit();
//输入框内为最新的签名
input.value = content.innerText;
});
进入编辑状态后,我们将输入框内的签名等于我们此时内容里的签名然后就可以继续添加两个按钮的点击事件了:
// 点击保存 保存新的签名
save.addEventListener('click',function(){
// 保存新的签名
content.innerText = input.value;
convertToText();
});
// 点击取消 退出编辑状态
cancel.addEventListener('click',function(){
convertToText();
});
在完成编辑后,点击按钮则会变回文本状态。这就是简单的利用dom编程来完成就地操作,但是这种方法在面试官中可能只能给到80分。因为这种方法并没有考虑到实际的业务需求中,在实际的业务中,你不可能每次需要完成一个就地编辑的功能就再把这些代码全部重新打一遍,所以我们需要用到面向对象的思想,封装一个用来就地编辑的JS文件以便于重复利用。
优化写法
创建一个editinplace.js文件,这时候就要用到我们上次讲的构造函数和原型的知识了,有不懂的小伙伴可以去看看详解JS中的构造函数与原型,首先我们创建一个构造函数,将函数的属性写在内部,将方法写在prototype对象中:
function EditInPlace(id,parent,value){
this.id = id; // 跨函数共享属性
this.parent = parent || document.body;
this.value = value || '这个家伙很懒,什么都没有留下';
this.createElement(this.id);// 在自己内部找不到去prototype上找
}
我们为父级容器和内容创建一个默认值,如果我们在传递参数时没有写入父容器和内容也就是||前面的值为空,则会将||后面的值设为默认值
接下来定义的这个原型方法的作用就是:自动创建一个div元素并将其id设置为参数传入的id,然后将其装进指定的父容器中,也就是创建一个新的DOM节点并将其挂载到现有的DOM树上;再创建一个span元素用于展示内容,值为参数传递进来的value,并将其嵌套进div元素中
EditInPlace.prototype.createElement = function(id){
// 下面的代码效果就是<div id=""><span></span></div>
this.containerElement = document.createElement('div');
this.containerElement.id = id;
this.parent.appendChild(this.containerElement);
this.staticElement = document.createElement('span');
this.staticElement.innerText = this.value;
this.containerElement.appendChild(this.staticElement);
}
appendChild 是 JavaScript 中用于将一个节点添加到另一个节点的子节点列表末尾的方法。
document.createElement 是 JavaScript 中用于创建新的 HTML 元素节点的方法。它属于 Document 接口的一部分,允许开发者动态地创建各种类型的 DOM 元素,并将其插入到现有的文档结构中。
最后我们在html文件中创建一个实例对象,并将js文件引入:
<body>
<div id="app">
</div>
<script src="./editInPlace.js"></script>
<script>
// 流程代码, 走向面向对象封装
new EditInPlace(
'ep1',
document.getElementById('app'),
'这个家伙很懒,什么都没有留下'
);
new EditInPlace(
'ep2',
document.getElementById('app'),
'好嗨哟'
);
</script>
</body>
这样优化写法的dom编程我们就完成了,还剩下监听点击事件我们就不多赘述了,写法与上面基本相同,只要封装在JS文件中就可以了,到这里为止面试官就该笑着让你回答下一题了。
小结
我们介绍了从传统表单提交到DOM编程实现就地编辑的进化,通过面向对象的方法优化代码结构,提升用户体验和代码复用性,拿下Bilibili的这道经典考题,希望能对大家有所帮助。