handsontable对单元格某个文字进行样式修改

337 阅读6分钟

前言

  • 常网IT源码上线啦!
  • 本篇录入技术选型专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
  • 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
  • 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。

你的建议对我来说非常宝贵,
我会好好考虑并付诸实践的。

2.jpg

压力--

一、前言

今天像往常一样,开开心心的打代码编程,我是属于那种爱思考的。

突然想handsontable对单元格某个文字进行样式修改,不知道能不能实现。

于是便有了今天这篇文章,一起来探讨一下。

当然,你也可以当做面试题。

二、handsontable

可能要先介绍一下他是是什么?

Handsontable 是一个功能强大的 JavaScript 表格库,旨在提供类似电子表格的用户体验。它适用于需要处理大量数据的应用程序,特别是在数据输入和编辑方面表现突出。

看中他一点是,可以把xlsx的表格,直接复制cv进来handsontable表格中。

而今天想对handsontable插件表格的某个单元格的某些文字进行样式修改。

11.jpg

查阅了文档,发现并没有这个方法提供。

三、现状

目前表格只支持对整个单元格修改样式修改

const ht = this.ht.hotInstance
ht.setCellMetaObject(c.row, c.col, {
  renderer: 'cenllitem',
  style: style	// style变量可以有color、border等等
})

原生并不支持对单元格某些内容进行单独样式修改。

只能手搞了。

四、思路

现在的div结构只有一个td

image.webp

而我们要实现这个功能,需要进行切割,动态改变div,也就是分成三段显示

我们改变了【报单】颜色变红色,结构如下

image.webp

确定了div的结构,寻找一下是否支持修改单元格的内容。

修改单元格内容。

setDataAtCell

hotInstance.setDataAtCell(0, 0, '222');

这种写法是可以的,但就是不支持html元素。

我传入该语法,他当纯文本展示,并不会帮我们解析。

hotInstance.setDataAtCell(0, 0, '<div>222</div>');

setDataAtCell不可行(×)

innerHTML

在我们学习html,我们想要修改内容,可以用innerHTML修改元素结构。

这里也可以用最原生的。

怎么拿到选中的文字

单元格内容,我通过鼠标选中【社区】,是否有这种事件可获取内容?

image.webp

可以的,通过绑定mouseup事件,利用以下代码可获取鼠标拖动选中的文本

window.getSelection().toString()

双击事件

我们想要在单元格选中文字,必定先进行鼠标双击来选文字。

添加afterOnCellMouseDown双击事件,在内部给document绑定mouseup鼠标拖动事件。

在我们移动鼠标的时候,获取选中的文字,选完之后,我们会去选颜色,此时离开了单元格,触发beforeOnCellMouseDown离开事件,离开的时候移除removeEventListener绑定mouseup事件,不然会一直触发鼠标mouseup事件,节省性能。

<hot-table :settings="htsettings" ref="ht" />

htsettings: {
  data: [],
  afterOnCellMouseDown: (event, coords, td) => {
    // 双击事件
    if (event.detail === 2) { // Check for double-click
      const cells = this.getSelectedCells()
      if(!(cells && cells.length)) return 
      const meta = this.$refs.ht.hotInstance.getCellMeta(cells[cells.length-1].row, cells[cells.length-1].col)
      const cell = this.config.cells.find(f => f.position.row == cells[cells.length-1].row && f.position.col == cells[cells.length-1].col)
      this.cell = {
        cell,
        meta
      }
      document.removeEventListener('mouseup', this.listenSelection);
      document.addEventListener('mouseup', this.listenSelection);
      // console.log('绑定');
    }
  },
  // 离开选中单元格时,触发
  beforeOnCellMouseDown: (event, coords, td) => {
    if (event.type === 'mousedown' && !event.target.classList.contains('htMaster') && event.detail === 1) {
      document.removeEventListener('mouseup', this.listenSelection);
      // console.log('解开');
    }
  }
},

getSelection = (cell) =>{
  let selection=window.getSelection();
  
  if(selection.toString() && cell && cell.cell && cell.meta){
    cell.cell.selection = selection.toString()
    cell.meta.selection = selection.toString()
  }
},

在上述代码,我们将鼠标选中的文字,调用getSelection方法,将存入单元格对象的selection字段中。

getSelection方法存meta、和cell都要。

22.jpg

设置颜色

我们在选颜色的时候,触发handleItemStyle方法,接收颜色值。

这里先调用ht.render(),再走this.$nextTick(()此时我们已经拼接好三段dom,赋值给innerText。

ht.setCellMetaObject而这段设置meta的样式,将原本样式(比如原本有加粗、居中)添加进去。

renderer: 'cenllitem',这里触发了自定义渲染器。往下看。

handleItemStyle(params, type = 'style') {
  const ht = this.ht.hotInstance
  const cells = this.getSelectedCells()	// 获取选中的单元格
  cells.forEach(c => {
    const meta = ht.getCellMeta(c.row, c.col)
    const cell = this.config.cells.find(f => f.position.row == c.row && f.position.col == c.col)

    // 如果有选中文字的,selection就有值
    if(cell.selection && params.color){
      this.$nextTick(()=>{ // 修改innerText,dom需要使用nextTick
        let td = ht.getCell(c.row, c.col);
        let content = td.innerText.split(cell.selection)	// 对选中文字,进行切割
        td.innerText = '';

        const start = document.createElement('span')
        start.innerText = content[0]

        const end = document.createElement('span')
        end.innerText = content[1]

        // 这是我们的选中的dom
        const text = document.createElement('span')
        text.innerText = cell.selection
        text.style = `color:${params.color};`


        const style = Object.assign({}, meta.style || {}, params || {})
        ht.setCellMetaObject(c.row, c.col, {
          renderer: 'cenllitem',
          style: style
        })

        td.appendChild(start)
        td.appendChild(text)
        td.appendChild(end)
      })
    }
  })
  ht.render()
},

renderer: 'cenllitem',这里触发了自定义渲染器。往下看。

这里对每个单元格进行特殊处理。

我们将选中的内容存在cell的selection。

每一次切割dom成三段,都是根据selection有值再切割的,而不会保存切割后的dom。

33.jpg

五、注意点

每一次切割dom成三段,都是根据selection有值再切割的,也就是实时切割,而不会保存切割后的dom。

当然,保存切割后的dom,也是一种实现方式。

存meta、和cell都要。

过程

双击事件进入触发afterOnCellMouseDown拿到选中文字,离开单元格触发beforeOnCellMouseDown解绑mouseup,再次进入单元格进行上面的操作。

选中文字,之后选中颜色,触发handleItemStyle切割dom三段添加到innerText中,有meta添加

renderer: 'cenllitem'触发自定义的渲染器cenllitem。

此时dom成功渲染三段。

离开单元格,又重新双击该单元格,触发自定义的渲染器cenllitem。而内部拿到cell的selection,执行三段dom渲染成功。

同理,切换上面的tab,也会执行触发自定义的渲染器cenllitem。而内部拿到cell的selection,执行三段dom渲染成功。

44.jpg

六、优化点requestAnimationFrame

我们在操作dom的时候,可以使用window.requestAnimationFrame了执行dom切割换颜色。

  • 可以帮助浏览器优化动画的渲染,因为它会在浏览器的下一个重绘周期中调用回调函数。这样可以确保动画平滑且效率更高。
  • 它使得动画与浏览器的刷新率(通常是60Hz,即每秒60帧)保持同步,从而减少了动画抖动和滞后,使得动画更加流畅。
  • 在浏览器标签页或窗口不可见时,requestAnimationFrame() 不会执行动画,从而节省了 CPU 和 GPU 资源。这有助于提高页面的性能和节电。
  • 相比于定时器方法(如 setTimeout()),requestAnimationFrame() 更能有效减少掉帧现象。定时器可能会导致动画帧的间隔不一致,从而影响动画的平滑度。

handsontable可能很小众,但他真的很强大,very。

至此撒花~

后记

我们在实际项目中或多或少遇到一些奇奇怪怪的问题。

自己也会对一些写法的思考,为什么不行🤔,又为什么行了?

最后,祝君能拿下满意的offer。

我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车

👍 如果对您有帮助,您的点赞是我前进的润滑剂。

以往推荐

小小导出,我大前端足矣!

靓仔,说一下keep-alive缓存组件后怎么更新及原理?

面试官问我watch和computed的区别以及选择?

面试官问我new Vue阶段做了什么?

前端仔,快把dist部署到Nginx上

多图详解,一次性啃懂原型链(上万字)

Vue-Cli3搭建组件库

Vue实现动态路由(和面试官吹项目亮点)

项目中你不知道的Axios骚操作(手写核心原理、兼容性)

VuePress搭建项目组件文档

原文链接

juejin.cn/spost/74180…