SOURCE

console 命令行工具 X clear

                    
>
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;
}

本项目引用的自定义外部资源