网页版模仿Excel
最近公司闲的dan疼,非要模仿Excel做一个网页版的Excel,刚开始听说要做这么一个东西的时候瞬间觉得公司领导高(sang)瞻(xin)远(bing)瞩(kuang),只能头铁的接下了,那就开始干。其实主要目的是为了能连上自己的数据库,方便操作表格,后来发现实现成本太高,做了一个前端DEMO就果断放弃了,但是这个DEMO还是有点意思,发开DEMO过程中有些心得与收获。
github地址
https://github.com/zhuqitao/E...
实现功能
- 字体、字号、颜色、背景色、加粗、斜体等字体样式基本操作功能
- 鼠标拖拽多选单元格
- 左右居中布局
- 合并、拆分单元格
- 插入函数(没有计算功能的函数,只是静态添加函数名)
- 本地打开excel(.xls)文件
截图
先看看目前的效果图:
滚动问题
领导说先做一个demo看看效果,那就亮出咱家的看家本领吧html+css+js三剑客,不就是表格吗,html填上table,修修样式,页面效果就ok了。
第一个碰到的问题就是滚动问题,看上面的截图就知道其实这是一种表格表头固定,表格第一列固定,其他部分滚动的效果,但是其实没有那么简单,还有一个问题是垂直滚动的时候第一行(表头)不动,但是第一列得跟着动啊,水平滚动亦是如此,这可怎么办啊,这种情况没遇到过啊,但是得解决啊。
不知道的网友们可以考虑考虑怎么做? 知道怎么处理这种情况的也别喷博主菜。
最后的解决方法就是写了三个table,一个放表头(第一行),一个放第一列,一个放表格主要内容,看下图:
table1放置表头(A,B,C),table2放置第一列,table3放置中间操作区域,把table1、2设置为overflow:hidden,只有table3可以滚动,监听table3的滚动事件,获取table3的scrollLeft和scrollTop然后分别给table1、table2的scrollLeft和scrollTop赋值。
鼠标按下拖拽多选单元格
这个是本DEMO唯一的亮点了(个人认为)。
表格目前定下的是30*30的表格。一开始的想法是监听鼠标按下(mousedown)事件获取鼠标按下时的位置,然后再监听鼠标移动事件(mousemove),获取鼠标当前的位置,然后根据当前位置和按下时位置画一个矩形,循环遍历所有的单元格元素,判断单元格是否在矩形内,然后给选中的单元格添加active类。
这样做的结果是页面非常卡,因为鼠标移动过程会多次触发鼠标移动事件(mousemove),会多次进行单元格元素循环遍历。 后来就不在鼠标移动事件里面进行单元格遍历操作,在鼠标松开事件里面遍历所有的单元格元素。
这样确实快了一些,但是还有一个问题上面两种方法都没解决的问题,就是如果选择了很多元素(比如100个),在遍历单元格元素的时候需要对着100个元素操作添加active类,其实js操作操作DOM是比较耗时的,这里也没有想到什么好的DOM操作缓存的方法。
就在一筹莫展的时候想到了目前流行的虚拟dom框架(react、vue等),果断使用了vue。在vue实例里创建两个对象数组,分别作为行数据和列数据,页面上分别对<tr>和<td>标签使用v-for循环渲染,这样就能生成一张表格。每个单元格都给加上行数和列树标志。
在鼠标按下获取鼠标按下时所在单元格的行数和列数,鼠标松开时获取松开时所在单元格的行数和列数,然后先循环行数据的数组,所有在选中的行数的数据对象添加选中标志,然后再循环列数据的数组,所有在选中的列数的数据对象添加选中标志,在页面对<tr>和<td>标签使用v-for循环渲染时动态绑定class类,只有行和列同时被选中,单元格才设置为选中状态,这样其实只循环了30+30=60次,瞬间把复杂度有原来的O(n^2)降低到O(2n)。
其实背后是通过vue的diff算法快速的计算出需要改变的dom元素,然后利用虚拟dom快速渲染到页面上,速度大大提升。这也让我真实的感受到这些框架的威力(怪不得这么火)。
我的语言表达能力不是很强,上面说的大家不知道有没有听明白,没明白的可以看看源代码(^︹^)。
意外收获
- 打开本地xls文件
有一款jquery插件js-xlsx可以帮助在线打开本地xls文件,并把xls文件转成json。
- 本地保存xls文件
利用xml的createElementNS创建带有指定命名空间的元素节点,再利用js自带Blob数据类型保存xls文件:
var urlObject = window.URL || window.webkitURL || window;
var export_blob = new Blob(['test']);
var save_link = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
var ev = document.createEvent("MouseEvents");
ev.initMouseEvent(
"click", true, false, window, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
save_link.href = urlObject.createObjectURL(export_blob);
save_link.download = 'test.xls';
save_link.dispatchEvent(ev);
- execCommand
当一个HTML文档切换到设计模式 designMode时,文档对象暴露 execCommand 方法,该方法允许运行命令来操纵可编辑区域的内容。详情查看MDN地址:https://developer.mozilla.org...
AbsolutePosition 设定元素的 position 属性为“absolute”(绝对)。
BackColor 设置或获取当前选中区的背景颜色。
BlockDirLTR 目前尚未支持。
BlockDirRTL 目前尚未支持。
Bold 切换当前选中区的粗体显示与否。
BrowseMode 目前尚未支持。
Copy 将当前选中区复制到剪贴板。
Paste 用剪贴板内容覆盖当前选中区(目前尚未支持)。
...
// 例子
document.execCommand("copy")
document.execCommand("cut")
document.execCommand('paste')
- window.getSelection()
获取鼠标选中的文本
- clipboarddata.setData()
手动设置剪切板,一般用于用户复制内容时添加版权信息。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。