接上节继续,在前端开发中,a链接常用a:visited伪类,用于区分链接点击过后的呈现效果。
在tauri项目模板中,把App组件中添加3个链接:
然后在App.css中添加几个样式:
程序跑起来后,却意外发现:其它几个伪类都起作用,唯独:visited这个伪类,在tairu桌面应用中一点反应都没有,参见下面,上面为tauri桌面应用,下面为浏览器窗口。
从动图效果看出,在浏览器中:visited是能正常生效的,点击后的链接显示为orange橙色,但是在上面的tauri应用中却没反应。其实不光是tauri有这个问题,electron同样也有类似问题。
StackOverFlow上有外国网友给出了解决思路:将访问过的链接,写入localStorage中,然后在组件生命周期的DidUnmount中,给这些链接,强行附加一个额外样式。
先定义2个常量,分别用于localStorage存放访问过的a链接记录,另1个则是强行附加的样式类名
const LOCAL_STORAGE_HISTORY_KEY = "visitedHistory";
const VISITED_CLASS_NAME = "visited";
核心代码1:setVisited
从localStorage中取出点击过的a链接记录(先不管如何存入localStorage的,后面会讲到,假如已经有了),然后判断记录是否过期,如果过期了则删除(重要!否则一直不停点,一直不停向localStorage中存,列表越来越大,早晚崩溃),然后将每个a链接的href跟访问记录匹配,对上了,则追加1个visited的样式(当然:这个样式要额外写)
/**
* 根据localStorage里的历史记录,将a附加上visited样式
* by 菩提树下的杨过http://yjmyzz.cnblogs.com/
*/
function setVisited() {
let localstorageSimuHistory = localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY);
let simuHistory = localstorageSimuHistory ? JSON.parse(localstorageSimuHistory) : [];
//过期访问记录清理
const now = new Date();
let hasExpired = false;
for (let i = simuHistory.length - 1; i >= 0; i--) {
let item = simuHistory[i];
//过期的访问记录删除
if (now.getTime() > item.expire) {
simuHistory.splice(i, 1)
hasExpired = true
}
}
if (hasExpired) {
if (simuHistory.length <= 0) {
localStorage.removeItem(LOCAL_STORAGE_HISTORY_KEY);
}
else {
localStorage.setItem(LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(simuHistory));
}
}
//遍历所有a,访问过的,则强制附加visited样式
let elements = document.getElementsByTagName('a');
for (let i = 0; i < elements.length; i++) {
for (let h = 0; h < simuHistory.length; h++) {
if (elements[i].href === simuHistory[h].url && elements[i].className.indexOf(VISITED_CLASS_NAME) === -1) {
elements[i].className += ` ${VISITED_CLASS_NAME}`;
}
}
}
}
localStorage中的数据,大致长这样:
核心代码2:addHref
每个链接点击后,将自身的href存入localStorage
/**
* a链接点击后将url加入localStorage
* @param url
*/
function addHref(url: String) {
let localstorageSimuHistory = localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY);
let simuHistory = localstorageSimuHistory ? JSON.parse(localstorageSimuHistory) : [];
let found = false;
const now = new Date();
//访问记录过期时间设置(此处仅为示例:30秒)
const ttl: number = 1000 * 30;
for (let i = simuHistory.length - 1; i >= 0; i--) {
let item = simuHistory[i];
if (item.url === url) {
found = true;
//过期时间续租
simuHistory[i] = { "url": url, "expire": new Date().getTime() + ttl };
break;
}
}
//如果本链接不在访问列表里,则添加
if (!found) {
simuHistory[simuHistory.length] = { "url": url, "expire": new Date().getTime() + ttl };
localStorage.setItem(LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(simuHistory));
}
//此处只是为了方便,把所有点过的a全刷了一把,还可以再优化下(略)
setVisited();
}
核心代码3:bindAddHref
给每个a链接的click绑定事件
function bindAddHref() {
let elements = document.getElementsByTagName('a');
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = () => {
addHref(elements[i].href);
}
}
setVisited();
}
有了上面3个核心方法,就可以在App的生命周期"DidMount"挂载阶段,调用bindAddHref,为每个a链接自动绑定click事件
当然App.css里得手动添加.visited样式
.visited {
color: orange !important
}
最后运行的效果如下:
从运行效果上看,"大致"跟浏览器上的表现相同,但值得说明的是,这毕竟只是一种变相的解决方法,二者还是有差别的,如果用开发者工具,把localStorage清了,而浏览器并不清除缓存(反过来也一样),二者的表现还是有差异,不过大多数情况下,上述解决方案,应该能满足业务要求了。
文中示例代码:https://github.com/yjmyzz/tauri-visited-solution