网站简单实现撤销操作(一)

  • 文章
  • 作者:Hubery
  • 发布时间:2020-05-20
  • 阅读数:273
  • 分类:前端
  • 标签: 前端

一、前言

    开发的过程中遇到一个需求,实现用户的撤销操作。撤销操作在各种编辑软件是常见的功能。

但我还是第一次接到这样的需求,这里记录下详细的实现过程。

clipboard1.png

二、需求分析

    需求是这样的,当用户点击操作中的 确认、删除、待定、词库等操作后,希望能够还原到原来的状态。(这里讲一下,当用户点击操作的按钮后是需要和后端进行交互,进行数据的更改。)

因为涉及到和后端进行数据交互,我第一反应是在后端进行记录数据的变动,在前端每次操作并且把操作的内容传到后端,但是后端把这些记录存在哪里,又什么时候把这些存的数据进行清除呢,想想有点头大,好像后端记录有点麻烦。

那么后端不好记录,就只能在前端实现了。

操作记录一般应该是链式的,如下图,那么这种数据结构的存储我们应该想到用队列和栈。

1589877607.png

  1、这里扩展介绍下队列和栈

      介绍队列和栈之前还需要学习下js中的数组,因为js中没有队列和栈这种数据结构,需要我们自己来实现

  a、数组介绍

详细介绍参考如下地址:

https://www.w3school.com.cn/jsref/jsref_obj_array.asp

原生js并没有这种队列和栈数据结构,那么就需要我们自己去实现,队列和栈其实都是线性数据接口,那么我们可以用数组来实现。

这里主要介绍几个在队列和栈中需要用到的几个数组对象方法,下面实现队列和栈时需要使用

pop()	删除并返回数组的最后一个元素
push()	向数组的末尾添加一个或更多元素,并返回新的长度。
shift()	删除并返回数组的第一个元素

  b、队列

队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表,队列是一种先进先出的线性表,简称FIFO,允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)。向队中插入元素称为进队,新元素进队后成为新的队尾元素;向队中删除元素称为出队,元素出队后,其后继元素就成为新的队头元素。

clipboard2.png

原生js实现队列,队列主要的特性是先进先出,所有需要使用到数据的push()方法从队尾插入数据,然后用shift()方法从队头取数据。实现如下

function Queue() {
     
    //初始化队列(使用数组实现)
    var items = [];

    //向队列(尾部)中插入元素
    this.push= function(element) {
        items.push(element);
    }

    //从队列(头部)中弹出一个元素,并返回该元素
    this.pop= function() {
        return items.shift();
    }

    //查看队列最前面的元素(数组中索引为0的元素)
    this.front = function() {
        return items[0];
    }

    //查看队列是否为空,如果为空,返回true;否则返回false
    this.isEmpty = function() {
        return items.length == 0;
    }

    //查看队列的长度
    this.size = function() {
        return items.length;
    }
     // 移除栈里所有的元素
    this.clear = function () {
        items = [];
    };

    //查看队列
    this.print = function() {
        //以字符串形势返回
        return items.toString();
    }
}

  c.栈

栈是限定仅在表尾进行插入和删除操作的线性表,我们把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈,栈又称后进先出的线性表,简称LIFO结构。(和队列正好相反

clipboard3.png

原生js实现栈,栈主要的特性是后进先出,所有需要使用到数据的push()和pop() 方法实现。在数组的一端进行存和取数据。

function Stack() {
    var items = [];

    // 添加一个(或几个)新元素到栈顶
    this.push = function (ele) {
        items.push(ele);
    };

    // 移除栈顶的元素,同时返回被移除的元素
    this.pop = function () {
        return items.pop();
    };

    // 返回栈顶的元素,不对栈做任何修改
    this.peek = function () {
        return items[items.length - 1];
    };

    // 判断栈里使是否还有元素
    this.isEmpty = function () {
        return items.length === 0;
    };

    // 移除栈里所有的元素
    this.clear = function () {
        items = [];
    };

    // 返回栈里的元素个数
    this.size = function () {
        return items.length;
    };
    // 移除一个元素
    this.shift = function () {
        return items.shift()
    }
}

三、需求的实现:

    上面扩展学习了栈和队列后,下面就是用栈来实现我们的需求。

回到我们的需求上,撤销操作时,取出的数据应该是后进入到容器中的,所以我们可以简单的用栈实现这一需求。也就是用户点击 确认、删除、待定、词库等操作后,把用户进行的操作的数据记录在栈里面,当撤回时再将其状态改为之前的状态。

这里主要注意的是记录还原状态需要的值,我这里记录了术语的id,目前的状态,改变后的状态、删除的html。

下面是一个简单的demo,因为我这里涉及的具体业务,还涉及到后端交互,就不贴原始代码了,使用时只需要在入栈时,把还原时需要记录的数据加入栈内就可以了。

下面是简单代码演示。

http://jsrun.net/JQ2Kp/edit


demo代码:

  • 这里的实现是比较简单的,存在着缺陷,比如不能把删除的内容还原的具体的位置。


<!DOCTYPE html>
<html>
<body>
    <button style="color: green">撤销</button>
        <div id='box'>
            <table>
                <tr>
                    <td>数据</td>
                    <td>操作</td>
                </tr>
                <tr>
                    <td>1</td>
                    <td><button>删除</button></td>
                </tr>
                <tr>
                    <td>2</td>
                    <td><button>删除</button></td>
                </tr>
                <tr>
                    <td>3</td>
                    <td><button>删除</button></td>
                </tr>
                <tr>
                    <td>4</td>
                    <td><button>删除</button></td>
                </tr>
                <tr>
                    <td>5</td>
                    <td><button>删除</button></td>
                </tr>
                <tr>
                    <td>6</td>
                    <td><button>删除</button></td>
                </tr>
                <tr>
                    <td>7</td>
                    <td><button>删除</button></td>
                </tr> 
            </table>
        </div>
    </body>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.js"></script>
    <script type="text/javascript">
    function Stack(num=5) {
        var items = [];
    
        // 添加一个(或几个)新元素到栈顶
        this.push = function (ele) {
            if(num.length >=num)
            {
                items.shift()
            }
            items.push(ele);
        };
        
        // 移除栈顶的元素,同时返回被移除的元素
        this.pop = function () {
            return items.pop();
        };
    
        // 返回栈顶的元素,不对栈做任何修改
        this.peek = function () {
            return items[items.length - 1];
        };
    
        // 判断栈里使是否还有元素
        this.isEmpty = function () {
            return items.length === 0;
        };
    
        // 移除栈里所有的元素
        this.clear = function () {
            items = [];
        };
    
        // 返回栈里的元素个数
        this.size = function () {
            return items.length;
        };
        // 移除一个元素
        this.shift = function () {
            return items.shift()
        }
    }
        var stack = new Stack(4)
         $(document).on('click','.del', function(){
            var _dict = {}
            var $this = $(this)
            var _html = '<tr>' + $this.parent().parent().html() + '</tr>'
            _dict['_html'] = _html
            stack.push(_dict)
            $this.parent().parent().remove()
        })

        $('.back').on('click', function(){
             if (stack.isEmpty()) {
                   alert('无撤销内容')
            } else {
               _html = stack.pop()._html
              $('#box table').find('tr:first-child').after(_html)
            }
        })
    </script>
</html>


评论列表
优秀的你不评论一下咩!!
新的评论