前言
作为一名前端"资深"切图仔,想要成为高级开发工程师的话,用户的体验感极为重要,今天我们就来学习如何是实现这道面试题,让用户感受js的高级用户体验——就地编辑。
业务场景
不知道jym是否发现,我们经常使用的b站,在b站的个人主页页面内,有一个个性签名,也就是下面这个,它就是用就地编辑功能,让用户随时都可以对自己的个性签名进行修改或删除,大大提高用户的使用体验。
- 就地编辑的需求分析:
- 平时是文本状态,显示签名
- mouseover 事件 进入编辑状态,显示编辑框
- 退出编辑状态时 自动保存,切换到文本状态
那这样就很简单了,创建所需的DOM元素,然后加上事件监听,修改DOM元素的展示状态,文本状态和编辑状态相互转换,就可以完成了文本和编辑的切换了。
实践了的jym就会发现,数据都没被记录下来,当页面刷新时,又回到了初始状态,所以,我们需要把输入的文本保存到数据库内。而文本内的数据显示数据库,所以我们现在需要一个模拟的数据库,需要用到json-server。
这里简单介绍一下:
json-server是一个简单的命令行工具,它用于创建一个假的API服务器,主要用于模拟JSON文件或数据库中的数据。以下是json-server的一些主要特点:
- 快速设置:只需要几分钟就可以启动并运行一个RESTful API(资源接口)。
- 静态JSON文件:你可以使用一个JSON文件作为数据源,
json-server会将其转换为API端点。 - 数据库支持:除了JSON文件,它还支持SQLite、MongoDB等数据库。
- 自动路由:根据你的数据结构自动生成GET, POST, PUT, DELETE等HTTP方法的路由。
- 延迟模拟:可以添加网络延迟,以模拟真实世界中的网络状况。
- 中间件支持:允许你添加自定义中间件来处理更复杂的逻辑。
要使用json-server,首先需要通过npm(Node.js包管理器)安装它。安装完成后,可以通过指定一个JSON文件或数据库来启动服务器。
-
初始化一个文件为仿后端数据库json文件:
- 创建一个后端文件夹
npm init -y初始化 package.json 配置npm i json-server安装json-server这个本地开发依赖- 在后端项目文件内创建一个模拟的数据库json文件
db.json - scripts 脚本区域
json-server --watch db.json(此处需要自己配置)这将启动一个监听在默认端口3000上的服务器 - 在后端文件终端下启动 -> npm run dev
-
下面是db.json内的文件,和访问的模拟API:
-
网页打开:
http://localhost:3000/users: -
如果要访问第一个数据对象,可以访问
http://localhost:3000/users/1
前端代码如下:
<body>
<div id="signature"></div>
<script src="./edit_in_place.js"></script>
<script>
fetch("http://localhost:3000/users/1")
.then(
res => res.json()
).then(data =>{
console.log(data,"////////")
const {signature} = data; // es6 的解构特性
new EditInPlace(document.getElementById('signature'));
})
</script>
</body>
解决刷新存储不到值的问题: 当我们刚打开页面时,可以向后端发送数据请求,在返回的json数据内找到你之前存储在数据库内的signature,将其显示,然后修改signature时直接将输入框内的值存入数据库,这样形成一个闭环,达到存储目的。
这里的fetch()函数是ES6新封装的异步请求方式,它是基于Promise的API,用于替代传统的XMLHttpRequest,后面会详细了解一些promise的,这里做简单了解。
那么我们再来看edit_in_place.js文件内容:创建DoM元素我们就不看了,上面有截图,全部代码以放最后。
-
构造函数初始化先创建了DOM元素节点,然后再进行事件监听,在创建DOM的时候默认初始化为文本状态的显示。
-
在事件监听函数内,一定要注意this的丢失情况,在事件监听时,一般可以利用箭头函数没this或使用的是外部的this的特性,或者在事件监听执行前对外部this进行存储,就像我的截图里一样。
-
因为
this在函数表达式中的绑定是在函数创建时确定的,而不是在调用时确定的。在这种情况下,this通常会绑定到全局对象(在浏览器中是window)或者在严格模式下('use strict';)为undefined。 -
这里即为保存按钮事件触发,将输入框的值赋给静态文本元素,然后由编辑状态转为文本状态,顺便更新数据库。
-
下面即为浏览器展示效果:
-
点击文本进行切换,
edit_in_place.js代码如下:
function EditInPlace(container,value = '这个家伙很懒,什么都没有留下'){
this.container = container;
this.value = value;
// 动态创建文本和编辑input和dom 封装,代码的管理好
this.createElement();
this.attachEvents();
}
EditInPlace.prototype = {
// 就地编辑的动态DOM
createElement: function(){
// 操作DOM树
// 创建一个div
this.editElement = document.createElement('div');
//添加一个子元素
this.container.appendChild(this.editElement);
// signature 文本值
this.staticElement = document.createElement('span');
this.staticElement.innerHTML = this.value;
this.editElement.appendChild(this.staticElement);
// input 输入框
this.fieldElement = document.createElement('input')
this.fieldElement.type = 'text';
this.fieldElement.value = this.value;
this.editElement.appendChild(this.fieldElement);
// 确定按钮
this.savaButton = document.createElement('input');
this.savaButton.type = 'button';
this.savaButton.value = '保存';
this.editElement.appendChild(this.savaButton);
// 取消按钮
this.cancelButton = document.createElement('input');
this.cancelButton.type = 'button';
this.cancelButton.value = '取消';
this.editElement.appendChild(this.cancelButton);
// 初始文本状态
this.converToText()
},
// 文本状态
converToText: function(){
this.staticElement.style.display = 'inline';
this.fieldElement.style.display = 'none';
this.savaButton.style.display = 'none';
this.cancelButton.style.display = 'none';
},
// 编辑状态
converToEdit: function(){
this.staticElement.style.display = 'none';
this.fieldElement.style.display = 'inline';
this.savaButton.style.display = 'inline';
this.cancelButton.style.display = 'inline';
},
// 事件监听
attachEvents: function(){
// this 指向外层
this.staticElement.addEventListener('click',()=>{
// this指向元素
this.converToEdit();
})
// let that = this;
// this.staticElement.addEventListener('click',function(){
// this丢失了
// that.converToEdit(); // 防止创建函数时this丢失
// })
this.savaButton.addEventListener('click',() => {
this.save();
})
this.cancelButton.addEventListener('click',()=>{
this.converToText();
})
},
save: function(){
this.value = this.fieldElement.value;
this.staticElement.innerHTML = this.value;
this.converToText();
this.saveData()
},
saveData:function(){
let value = this.value;
// GET-->读 POST-->创建 PUT --> 更新 PATCH --> 局部更新 DELETE --> 删除
fetch('http://localhost:3000/users/1',{
method: 'PATCH',
headers: { // 请求头 发送的内容 以json格式发送
'Content-Type': 'application/json'
},
body: JSON.stringify({ // 请求体
signature: value
})
})
.then(res => res.json())
.then(data=>{
console.log(data,'保存成功');
})
}
}
总结
总算是说完了,感谢jym的观看哦,看到这里你已经很牛了,虽然文字有点多,但是我们都能从中学到很多,前端嘛,就该给用户更高级的体验感,这样才能留住用户。可能方法不是最好最优,以后慢慢更改,慢慢积累吧,让我们一起冲起来!