前言
最近在做一个日志管理的功能,其中有一个功能是这样的,在一个页面上会显示千上万条日志,
需要做一个搜索的功能,并能将搜索结果一一显示在视口中,通过控制滚动条。
这里使用html+原生js实现了一个简单的demo,核心步骤都是已实现。这里稍作一下记录。
技术实现
主要技术点是,获取装载所有日志的容器的innerHTML,然后使用js的replace搭配正则表达式,对内容进行搜索替换。
搜索替换的核心代码
const newHtml = logHtml.replace(reg, res => {
searchNum++
return `<span class="search-res">${res}</span>`
})
logHtml变量 日志容器的innerHTML
reg变量 要搜索的关键词正则表达式
searchNum变量 用来存放搜索结果的个数
css类search-res 用于高亮搜索结果和查询搜索结果
滚动到第num个搜索结果的核心代码
通过css类search-res来获取所有的搜索结果,并使用num来获取具体要显示的搜索结果。
要显示第num个搜索结果,需要将日志容器的滚动条scrollTop(元素到父元素顶部的高度),设置为搜索结果元素的offsetTop减去日志容器视口高度的一半。
const currentEl = document.querySelectorAll(".search-res")[num - 1]
document.querySelector("#log-container").scrollTop = currentEl.offsetTop - document.querySelector("#log-container").offsetHeight / 2
document.querySelector("#current-num").innerText = currentNum
注意:每次在innerHTML中搜索,都是日志容器的原始innerHTML。即在没有搜索前保存的innerHTML,日志数据不会,它就不会改变。
如果每次搜索前,取当前的日期容器innerHTML,那么就会保留上一次搜索的结果。
显示效果
点击enter键,跳到下一个搜索结果。
显示效果如上图,
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>日志搜索</title>
<style type="text/css">
* {
padding: 0;
margin: 0;
outline: none;
}
html,
body {
height: 100%;
font-family: "楷体", "楷体_GB2312";
}
.container {
height: 100%;
width: 1000px;
border: 1px solid #ddd;
margin: 0 auto;
box-sizing: border-box;
padding: 12px;
position: relative;
}
.title {
text-align: center;
font-size: 24px;
}
.margin-8 {
margin: 8px;
}
.log-container {
text-align: left;
overflow-y: auto;
height: calc(100% - 40px);
}
.search-wrap {
position: absolute;
padding: 0 8px;
right: 0px;
width: 200px;
height: 34px;
color: #616161;
transform: translateY(0);
background-color: #f3f3f3;
box-shadow: 0 0 8px 2px rgb(0 0 0 / 16%);
line-height: 32px;
}
.keyword-input {
background-color: rgb(255, 255, 255);
color: rgb(97, 97, 97);
height: 24px;
outline: none;
border: none;
padding: 0;
box-sizing: border-box;
border-radius: 4px;
display: inline-block;
width: 120px;
padding-left: 5px;
}
.log-row {
font-size: 14px;
line-height: 25px;
}
.search-res {
background-color: #f73131;
border-radius: 2px;
}
.search-res.active {
background-color: #a8ac94;
}
</style>
</head>
<body>
<div class="container">
<p class="title margin-8">日志搜索定位</p>
<div class="search-wrap">
<input id="keyword" class="keyword-input" value="三两" />
<span id="current-num">0</span>/<span id="search-num">0</span>
</div>
<div class="log-container" id="log-container">
</div>
</div>
<script>
let logHtml = ''
let searchNum = 0
let currentNum = 0
let halfOffsetHeight
function createLog(num = 100) {
const logArr = []
for (let i = 1; i < num; i++) {
if (i % 20 === 0) {
logArr.push(`<p class="log-row">${i} 竹外桃花三两枝,春江水暖鸭先知。蒌蒿满地芦芽短,正是河豚欲上时。</p>`)
} else {
logArr.push(`<p class="log-row">${i} 千山鸟飞绝,万径人踪灭。孤舟蓑笠翁,独钓寒江雪。</p>`)
}
}
return logArr.join('')
}
function appendHtml() {
const logHm = createLog(200)
logHtml = document.querySelector('#log-container').innerHTML
logHtml = logHtml + logHm
document.querySelector('#log-container').innerHTML = logHtml
}
function searchWord(reg) {
searchNum = 0
const newHtml = logHtml.replace(reg, res => {
searchNum++
return `<span class="search-res">${res}</span>`
})
document.querySelector('#log-container').innerHTML = newHtml
document.querySelector("#search-num").innerText = searchNum
currentNum = currentNum + 1
slide(currentNum)
}
function slide(num) {
const currentEl = document.querySelectorAll(".search-res")[num - 1]
if (!currentEl) {
return false
}
const currentOffsetTop = currentEl.offsetTop
document.querySelector("#log-container").scrollTop = currentOffsetTop - halfOffsetHeight
document.querySelector("#current-num").innerText = currentNum
}
function init() {
appendHtml()
halfOffsetHeight = document.querySelector("#log-container").offsetHeight / 2
document.querySelector("#keyword").addEventListener('keydown', function (e) {
if (e.keyCode === 13) {
const word = e.target.value
const reg = new RegExp(`${word}`, 'g')
// TODO 相同的搜索词,应该直接调用slide
searchWord(reg)
}
})
}
init()
</script>
</body>
</html>
总结
如果读者有更好的方案,欢迎在评论区留言。这是一次比较简单的尝试。
除此之外你还可以使用monaco编辑器来显示日志,搜索,以及着色。
对于使用虚拟滚动来实现日志展示的,不置与否。
只有最合适的技术,没有最优秀的技术。