console
let cvs = document.createElement('canvas');
let ctx = cvs.getContext('2d');
function measure(str, font) {
if (font) {
ctx.font = font;
}
return ctx.measureText(str).width;
}
let vm = new Vue({
el: '#root',
data: {
msg: '',
tokens: [],
items: [
"我是010",
"我是020",
"我是030",
"我是040",
"我是050",
]
},
methods: {
getRef(key) {
return this.$refs[key];
},
deep(node, callback) {
if (typeof callback == 'function') {
callback(node);
}
for (let i = 0; i < node.childNodes.length; ++i) {
let ch = node.childNodes[i];
this.deep(ch, callback);
}
}
},
watch: {
msg(newvalue, oldvalue) {
this.tokens = []
let node = this.getRef('content');
let searchText = this.msg;
if (!searchText) {
node.scroll(0, 0);
return;
}
let arr = [];
this.deep(node, (e) => {
if (e instanceof Text && e.textContent.includes(searchText)) {
arr.push(e);
}
});
arr.forEach((item, index) => {
let text = item.textContent;
for (let i = 0; i < text.length; ++i) {
let str = text.slice(i, i + searchText.length);
let startOffset = i;
let endOffset = i + searchText.length;
if (str == searchText) {
let range = new Range();
range.setStart(item, startOffset);
range.setEnd(item, endOffset);
let rect = range.getBoundingClientRect();
let crect = node.getBoundingClientRect();
let x = rect.left - crect.left;
let y = rect.top - crect.top;
this.tokens.push({
left: x.toFixed(0) + 'px',
top: y.toFixed(0) + 'px',
width: rect.width.toFixed(0) + 'px',
height: rect.height.toFixed(0) + 'px',
el: item.parentElement
});
}
}
});
if (this.tokens.length) {
setTimeout(() => {
let target = this.tokens[0].el;
let top = target.offsetTop;
node.scroll(0, top)
}, 200)
}
}
}
})
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=, initial-scale=">
<meta http-equiv="X-UA-Compatible" content="">
<title></title>
</head>
<body>
<div id="root">
<input type="text" v-model="msg" placeholder="搜索选项">
<div class="content" ref="content">
<div class="item" v-for="(item,index) in items" :key="index">{{item}}
<div class="sub">我是子项01</div>
<div class="sub">我是子项02</div>
<div class="sub">我是子项03</div>
</div>
<div class="token" v-for="(item,index) in tokens" :style="item"></div>
</div>
</div>
</body>
</html>
#root {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
#root div.item {
position: relative;
line-height: 50px;
padding-left: 100px;
z-index: 1;
}
#root div.item .sub {
padding-left: 50px;
}
.token {
position: absolute;
background: rgba(31, 211, 172, 0.5);
}
.content {
position: relative;
height: 300px;
overflow: auto;
background: #fff;
border: 1px solid #999;
box-sizing: border-box;
}
input {
width: 100%;
height: 30px;
outline: none;
}