由于要实现表情功能和@功能研究了二天,基本已经实现记录一下。
功能需求:实现发布和回复框的@功能和表情插入功能
用到的工具:主要是前端的js,我用的vue和jq的结合,因为很多包的实现本身就依赖jq的。表情功能jq插件emoji_picker,地址emoji-picker,@功能用的tribute.js地址tribute,他还有一个基于jq的包叫At.js感兴趣可以自行研究一下。之所以用tribute是因为At.js已经不维护了。
使用中遇到的问题:最主要的是要把二个插件结合起来用到vue的实例里面。这个是在我用官方demo实验的时候遇到的最头疼的问题。在vue实例里面就想尽量的用vue的方式尽量少的去对dom直接操作。二个插件都是初始化的时候动态的插入dom里面的,也就是根本没有办法去绑定数据。
解决方案:我这边后端的是服务器渲染用的jinja2,然后前端数据处理渲染用vue和jq结合。我想到的方法是曲线救国的方式因为emoji_picker的初始化插入会依赖我们自己HTML里面的一个textarea,有这个基础就可以在这里做文章了,基本思路是在html模板里面对这个textarea用v-mode绑定一下我这里绑定的是commentContent,因为后面emoji-picker初始化插入的时候其实会隐藏我们这个textarea转而显示页面的是他插入的dom输入框和emoji工具图标,但是他会监听输入框的变化把输入框的内容原模原样的克隆一份到我们在模板里面创建的textarea里面,简单的说就是如果你取消textarea的display:none这个的时候回显示二个输入框,有图有真相
你在emoji-picker里面输入任何东西textarea里面也会有一样的的内容。这样就好办了,因为我们绑定了textarea,所以在vue里面直接就可以拿到这个内容了。然后继续入坑,你会发现tribute他必须要你的输入框有一个id属性。问题是我们实际前端显示的输入框不是textarea而是emoji-picker动态添加的输入框。所以我们要想用tribute就需要后期自己也动态的给这个dom添加一个id。我的方法是在vue的mount里面用jq动态的添加id后再初始化tribute。但这里有一个坑,那就是有可能vue的实例化渲染已经完成了但是emoji-picker还没有完成初始化插入。也可能是我的页面需要插入的输入框有好几个的原因,反正我一开始的时候始终不能初始化tribute。原因就在这里。然后我用了一个延迟初始化,setTimeout一下就可以了,然后就可以初始化成功了。到这里还没有完,还有坑我想这个@的列表数据总是从后台取回来的吧,然后我在他的github找到了ajax取数据的方法,也模拟成功了,但是问题是我总不能一下把所有数据全部拉回来吧,所以还是要对数据分页一步一步的append进去吧,然后在他的github看见二个方法插入数据一个append和appendCurrent,问题是我怎么弄都不成功。也许是我的姿势不对,如果谁知道麻烦告诉我。不过我还是用了曲线方式解决了先看一下初始化代码吧
initAtJs() {
//动态插入id
$('#input-comment').next().attr("id", "_input-comment")
$('#input-reply').next().attr("id", "_input-reply")
$('#input-forward').next().attr("id", "_input-forward")
//这二个addEventListener是二个测试,目前没有用到
document.getElementById(
'_input-comment').addEventListener('tribute-replaced', function (e) {
console.log('Original Event:', e.detail.event);
console.log('Matched item:', e.detail.item);
});
document.getElementById(
'_input-comment').addEventListener('tribute-no-match', function (e) {
console.log('No match found!', e);
});
//这里是ajax取数据的模拟我这里还没有写ajax代码,我用的是一个window全局Array对象模拟的数据,
// 后期的数据取到后也可以直接操作这个全局Array对象就可以,直接concat到这个里面或者删除
var remoteSearch = function (text, cb) {
cb(atChoiceData)
}
//这里是初始化tribute
var tribute = new Tribute({
menuContainer: document.getElementById('content'),
values: function (text, cb) {
remoteSearch(text, users => cb(users));
},
lookup: 'key',
fillAttr: 'value',
selectTemplate: function (item) {
if (typeof item === 'undefined' || atList.indexOf(`@${item.original.value}`) >= 0) return null;
if (this.range.isContentEditable(this.current.element)) {
let tpl = `@${item.original.value}`
console.log(tpl);
//这里也是二个window全局Array对象Array,作用是把@的html和@的字符串分别用Array对象保存
// 后期提交后端时候用,只需要操作这二个对象就可以了,目前这些还只是模拟成功了
atList.push('@' + item.original.value)
atHtmlList.push(tpl)
return tpl
}
return '@' + item.original.value;
}
});
tribute.attach($("#_input-comment"));
tribute.attach($("#_input-reply"));
tribute.attach($("#_input-forward"));
if (tribute.isActive) {
console.log('Somebody is being mentioned!');
} else {
console.log("Who's this guy talking to?");
}
},
在mounted里面setTimeout调用这个初始化函数就可以
setTimeout(() => {
this.initAtJs()
}, 300)
写到这里其实也基本可以,只是你会发现还有一个坑。那就是在删除的时候怎么知道他什么时候删除的什么,我又去github看了,但是没有找到这个删除事件的钩子函数或者回调函数。我看见有人也提过同样的问题,官方的答复是目前没办法支持。
所以所以。。。。。。。只能自己想办法了。说一下我的解决办法,我的解决思路有二个,一个是如果你只在乎结果的话那么就可以不用关心她删除的哪一个以及什么时候删除的,甚至连atList和atHtmlList这二个对象都不需要。只需要在最后提交后台的时候去dom里面去取就可以。在emoji-picker插入的div里面直接用jq取,简单粗暴也快捷省去中间过程。怎么取?好吧我的代码贴出来:
let atList = []
let atHtmlList = []
//判断是哪一个
let attrId = this.commentModalShow
? "#_input-reply" : this.forwardModalShow
? "#_input-forward" : "#_input-comment"
//取值
$.each($(attrId).find('[contenteditable="false"]'), (index, item) => {
atList.push(item.innerText)
atHtmlList.push(item)
})
然后是另外一个思路,那就是也许你会在他删除的时候想做点别的什么?好吧,万能的vue里面的watch这时候派上用场了。在watch里面监听我们之前绑定在textarea的commentContent。这样就可以直接在这里对变化做操作了,只是这里就需要用到我们前面那二个array对象了,需要注意的是,由于删除的时候有时候删除一个有时候可能一次删除二个@块,而且删除动作比较快。也就是二次删除没有触发emoji-picker的同步内容事件的时候可能会不一样。他同步的事件是在输入框失去焦点的时候触发的。我没有看源码。如果有误望见谅。需要判断一下对象里面的数量和正则匹配commentContent的数量是不是相等。正则匹配commentContent小于我们的保存内容的数量的时候就说明有删除了。
if (this.commentContent.match(/[@]([^@](.*?))[\s]/g).length !== atHtmlList.length){
//正则匹配判断不相等后这里面可以写自己需要操作的了}
最后的最后就是一个tribute的小问题,就是在弹出选择@人的列表时候如果你去点击了导航条会出现控制台提示错误。官方也知道有这个问题,但是他们一直说的是你只需要竖版滚轮拉滚动条而不是用鼠标点击滚动条。我也没有搞懂他们啥意思。为啥要用这样反人类的操作。我在源代码里面做了一点小小的改变。可以在源代码里面搜索“cannot find the
这样就可以了不会抛出错误了。有共同学习讨论的小伙伴可以加个微信一起讨论学习共同成长,我的微信:WFpHMTY4MTc4(base64一下)