前言
- 常网IT源码上线啦!
- 本篇录入技术选型专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
- 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
- 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。
你的建议对我来说非常宝贵,
我会好好考虑并付诸实践的。
压力--
一、前言
今天像往常一样,开开心心的打代码编程,我是属于那种爱思考的。
突然想handsontable对单元格某个文字进行样式修改,不知道能不能实现。
于是便有了今天这篇文章,一起来探讨一下。
当然,你也可以当做面试题。
二、handsontable
可能要先介绍一下他是是什么?
Handsontable 是一个功能强大的 JavaScript 表格库,旨在提供类似电子表格的用户体验。它适用于需要处理大量数据的应用程序,特别是在数据输入和编辑方面表现突出。
看中他一点是,可以把xlsx的表格,直接复制cv进来handsontable表格中。
而今天想对handsontable插件表格的某个单元格的某些文字进行样式修改。
查阅了文档,发现并没有这个方法提供。
三、现状
目前表格只支持对整个单元格修改样式修改
const ht = this.ht.hotInstance
ht.setCellMetaObject(c.row, c.col, {
renderer: 'cenllitem',
style: style // style变量可以有color、border等等
})
原生并不支持对单元格某些内容进行单独样式修改。
只能手搞了。
四、思路
现在的div结构只有一个td
而我们要实现这个功能,需要进行切割,动态改变div,也就是分成三段显示
我们改变了【报单】颜色变红色,结构如下
确定了div的结构,寻找一下是否支持修改单元格的内容。
修改单元格内容。
setDataAtCell
hotInstance.setDataAtCell(0, 0, '222');
这种写法是可以的,但就是不支持html元素。
我传入该语法,他当纯文本展示,并不会帮我们解析。
hotInstance.setDataAtCell(0, 0, '<div>222</div>');
setDataAtCell不可行(×)
innerHTML
在我们学习html,我们想要修改内容,可以用innerHTML修改元素结构。
这里也可以用最原生的。
怎么拿到选中的文字
单元格内容,我通过鼠标选中【社区】,是否有这种事件可获取内容?
可以的,通过绑定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都要。
设置颜色
我们在选颜色的时候,触发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。
五、注意点
每一次切割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渲染成功。
六、优化点requestAnimationFrame
我们在操作dom的时候,可以使用window.requestAnimationFrame
了执行dom切割换颜色。
- 可以帮助浏览器优化动画的渲染,因为它会在浏览器的下一个重绘周期中调用回调函数。这样可以确保动画平滑且效率更高。
- 它使得动画与浏览器的刷新率(通常是60Hz,即每秒60帧)保持同步,从而减少了动画抖动和滞后,使得动画更加流畅。
- 在浏览器标签页或窗口不可见时,
requestAnimationFrame()
不会执行动画,从而节省了 CPU 和 GPU 资源。这有助于提高页面的性能和节电。 - 相比于定时器方法(如
setTimeout()
),requestAnimationFrame()
更能有效减少掉帧现象。定时器可能会导致动画帧的间隔不一致,从而影响动画的平滑度。
handsontable可能很小众,但他真的很强大,very。
至此撒花~
后记
我们在实际项目中或多或少遇到一些奇奇怪怪的问题。
自己也会对一些写法的思考,为什么不行🤔,又为什么行了?
最后,祝君能拿下满意的offer。
我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车
👍 如果对您有帮助,您的点赞是我前进的润滑剂。