最近修改QT项目,需要实现一个搜索高亮的功能,实现方式是匹配到关键字,通过span上写样式然后包裹关键字
qml
<span style="background-color:#fef08a">keyword</span>
实现效果如下

一、页面结构
页面主要由搜索框和结果列表组成。
yaml
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 12
TextField {
id: searchField
Layout.fillWidth: true
placeholderText: "请输入关键字"
}
ListView {
id: resultList
Layout.fillWidth: true
Layout.fillHeight: true
model: filteredItems
delegate: Text {
width: resultList.width
text: highlighted(modelData.content)
textFormat: Text.RichText
wrapMode: Text.Wrap
}
}
}
因为高亮内容使用了 span 标签,所以 Text 需要设置富文本:
vbnet
textFormat: Text.RichText
否则页面会直接显示 HTML 标签,无法渲染高亮样式。
准备测试数据:
css
property var sourceItems: [
{ content: "QML 是一种声明式界面开发语言" },
{ content: "使用 QML 可以快速开发 Qt 界面" },
{ content: "Text.RichText 支持简单的 HTML 标签" }
]
property var filteredItems: [] // 筛选列表
readonly property string keyword: searchField.text // 搜索关键字
页面加载完成后显示全部数据:
scss
// 搜索方法
Component.onCompleted: updateResults()
二、搜索监听
监听搜索框的 textChanged 信号:
yaml
TextField {
id: searchField
Layout.fillWidth: true
placeholderText: "请输入关键字"
onTextChanged: updateResults()
}
搜索方法如下:
scss
function updateResults() {
const target = searchField.text.toLocaleLowerCase()
if (!target.length) {
filteredItems = sourceItems.slice(0)
return
}
filteredItems = sourceItems.filter(function(item) {
const content = String(item.content || "").toLocaleLowerCase()
return content.indexOf(target) !== -1
})
}
搜索内容和原始内容统一转换成小写,因此匹配时不区分大小写。
当搜索框为空时:
ini
filteredItems = sourceItems.slice(0)
列表会重新显示全部数据。
三、高亮实现
首先对原始文本中的 HTML 特殊字符进行转义:
javascript
function escapeHtml(value) {
return String(value === undefined || value === null ? "" : value)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
}
然后实现关键字高亮:
vbnet
function highlighted(value) {
const text = String(value === undefined || value === null ? "" : value)
if (!keyword.length)
return escapeHtml(text)
const source = text.toLocaleLowerCase()
const target = keyword.toLocaleLowerCase()
let result = ""
let start = 0
let index = source.indexOf(target)
while (index !== -1) {
result += escapeHtml(text.slice(start, index))
result += "<span style='background-color:#fef08a'>"
+ escapeHtml(text.slice(index, index + keyword.length))
+ "</span>"
start = index + keyword.length
index = source.indexOf(target, start)
}
return result + escapeHtml(text.slice(start))
}
高亮实现的核心代码是:
less
result += "<span style='background-color:#fef08a'>"
+ escapeHtml(text.slice(index, index + keyword.length))
+ "</span>"
indexOf() 用于获取关键字位置,slice() 截取匹配到的原始文字,再通过带背景颜色的 span 标签进行包裹。
while 循环会继续查找下一处匹配,因此一段文字中出现多个相同关键字时,可以全部高亮显示。