模拟实现bilibili就地编辑功能

564 阅读3分钟

在现代网页开发中,用户体验(UX)是至关重要的。B站的就地编辑(EditInPlace)功能是一个典型的例子,它允许用户在不离开当前页面的情况下,直接编辑和保存内容。这种功能在很多平台上都很常见,可以显著提升用户的交互体验。本文将详细讨论如何实现这一功能。 bilibili.gif

用户体验

就地编辑的用户体验包括以下几个方面:

  1. 平时文本状态:默认情况下显示签名等静态文本。
  2. 编辑状态:鼠标悬停或点击时进入编辑状态,显示编辑框。
  3. 退出编辑状态:自动保存修改并切换回文本状态。

这种交互模式可以让用户在不离开当前页面的情况下,快速编辑和保存信息,提高用户的操作效率和满意度。

实现思路

实现就地编辑功能需要考虑以下几个技术点:

  1. 事件处理:通过事件监听来切换编辑和文本状态。
  2. 类的封装:使用类来封装功能,以便复用和维护。
  3. 本地存储:利用localStorage保存编辑内容。
  4. 异步请求:模拟后端数据,通过fetch获取和更新数据。

代码实现

HTML结构

首先,我们需要一个简单的HTML结构来挂载我们的就地编辑功能:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>就地编辑</title>
</head>
<body>
    <div id="signature"></div>
    <script src="./edit_in_place.js"></script>
    <script>
        // 初始化就地编辑功能
        const STORAGEKEY = 'signature';
        fetch("http://localhost:3000/users/1")
            .then(res => res.json())
            .then(data => {
                const { signature } = data;
                const stoSignature = localStorage.getItem(STORAGEKEY);
                if (signature !== stoSignature) {
                    localStorage.setItem(STORAGEKEY, signature);
                }
                new EditInPlace(STORAGEKEY, document.getElementById('signature'), signature);
            });
    </script>
</body>
</html>

EditInPlace 类

我们使用ES5的构造函数来创建EditInPlace类,并定义其方法:

function EditInPlace(storageKey, container, value = '编辑个性签名') {
    this.storageKey = storageKey;
    this.container = container;
    this.value = value;
    this.createElement();
    this.attachEvents();
}

EditInPlace.prototype = {
    createElement: function() {
        //创建一个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.saveButton = document.createElement('input');
        this.saveButton.type = 'button';
        this.saveButton.value = '保存';
        this.editElement.appendChild(this.saveButton);
        
        //取消按钮
        this.cancelButton = document.createElement('input');
        this.cancelButton.type = 'button';
        this.cancelButton.value = '取消';
        this.editElement.appendChild(this.cancelButton);
        
        //初始化文本状态
        this.convertToText();
    },
    //文本状态 
    convertToText: function() {
        this.staticElement.style.display = 'inline';
        this.fieldElement.style.display = 'none';
        this.saveButton.style.display = 'none';
        this.cancelButton.style.display = 'none';
    },
    //编辑状态
    convertToEdit: function() {
        this.staticElement.style.display = 'none';
        this.fieldElement.style.display = 'inline';
        this.saveButton.style.display = 'inline';
        this.cancelButton.style.display = 'inline';
    },
    //事件监听
    attachEvents: function() {
        const self = this;
        this.staticElement.addEventListener('click', function() {
            self.convertToEdit();
        });
        this.saveButton.addEventListener('click', function() {
            self.save();
        });
        this.cancelButton.addEventListener('click', function() {
            self.cancel();
        });
    },
    save: function() {
        this.value = this.fieldElement.value;
        this.staticElement.innerHTML = this.value;
        localStorage.setItem(this.storageKey, this.value);
        this.convertToText();
        this.saveData();
    },
};

使用箭头函数优化

注意在上述代码中的attachEvents函数中的回调函数我们使用了传统的函数表达式,因此this会丢失,所以我们要通过self保存this的引用。实际上,使用箭头函数可以更简洁地处理this指向问题,因为箭头函数不会创建自己的this,而是会捕获其所在上下文的this值。

EditInPlace.prototype.attachEvents = function() {
    this.staticElement.addEventListener('click', () => {
        this.convertToEdit();
    });
    this.saveButton.addEventListener('click', () => {
        this.save();
    });
    this.cancelButton.addEventListener('click', () => {
        this.coverToText();
    });
};

本地存储

利用localStorage可以将用户的编辑内容保存在本地,当用户刷新页面时,仍然可以看到已保存的内容。

save: function() {
    this.value = this.fieldElement.value;
    this.staticElement.innerHTML = this.value;
    localStorage.setItem(this.storageKey, this.value);
    this.convertToText();
},

模拟后端

为了模拟后端数据,我们可以使用json-server。首先初始化项目:

npm init -y
npm install json-server

package.json中添加脚本:

"scripts": {
    "dev": "json-server --watch db.json"
}

然后创建db.json文件:

{
    "users": [
        {
            "id": 1,
            "signature": "这个家伙很懒,什么也没有留下"
        }
    ]
}

运行json-server

npm run dev

这会启动一个本地服务器,可以通过http://localhost:3000/users/1访问用户数据。

我们也可以再写一个saveData函数来将更新的数据同步保存到后端数据库。

saveData: function(){
        let value = this.value
        //修改资源 
        fetch('http://localhost:3000/users/1',{
            method:'PATCH',
            //请求头,发送的内容 格式是json
            headers: {
                'Content-Type': 'application/json'
            },
            //请求体
            body: JSON.stringify({
                signature: value
            })
        })
        .then(res => res.json())
        .then(data => {
            console.log(data,'保存成功');
        })
    }

总结

通过本文的介绍,我们详细讲解了如何在网页中实现B站的就地编辑功能。我们不仅探讨了用户体验的设计,还涉及了JavaScript的高级技巧,如事件处理、类的封装和本地存储。希望通过本文的讲解,你能够更好地理解和应用这些技术,提升网页开发的效率和用户体验。