menu search

使用标签来过滤文章

给归档页面做了些许修改,感觉之前的样式太过于单调

Aug 23, 2017

自己其实也不知道该如何形容这种操作:基本上是在归档页面放了个标签云,点击标签后从文章列表中过滤出包含该标签的文章。

这么做的主要优点是点击标签后无需再次等待页面加载,可以节省资源,提高找文章的速度。

演示:归档


由于每个人的归档页面都不太相同,在这里说下思路,并在最后放上一个简单的例子。

  1. 通过data属性来输出文章的标签
  2. 遍历并创建一个包含了每个文章ID和标签的数组
  3. 使用filter()来筛选文章
  4. 隐藏其余的文章

HTML

<div id="archives">
	<div class="archive-group">
		<h3 class="archive-date">2017 / 08</h3>
		<div class="postList">
			<div class="postList--item" id="postOne" data-tag='["steam", "gaming"]'>Post with tag Steam and Gaming</div>
		</div>
	</div>
</div>
<div id="tagCloud">
	<a data-id="steam">Steam</a>
	<a data-id="programming">Programming</a>
</div>

大概就是这样,文章通过div.postList--item输出,元素的ID对应了文章的ID,并通过data-tag输出文章的标签。

JavaScript

// Author: Jimmy Cai (jimmehcai@gmail.com)
let posts = [];

document.querySelectorAll('.postList--item').forEach((item) => {
    let object = {
        "id": item.id
    };
    if (item.dataset.tag) {
        object.tag = JSON.parse(item.dataset.tag.toLowerCase())
    };
    posts.push(object);
});

function cleanUp() {
    document.querySelectorAll('.archive-group.hasMatch').forEach((group) => {
        group.classList.remove('hasMatch');
    });

    document.querySelectorAll('.postList--item.matched').forEach((post) => {
        post.classList.remove('matched');
    });

    document.getElementById('archives').classList.remove('searching');
};
document.querySelectorAll('#tagCloud a').forEach((item) => {
    item.addEventListener('click', (e) => {
        e.preventDefault();

        cleanUp();

        if (e.target.classList.contains('clicked')) {
            e.target.classList.remove('clicked');
            return;
        };

        if (document.querySelectorAll('#tagCloud a.clicked').length) {
            document.querySelectorAll('#tagCloud a.clicked')[0].classList.remove('clicked');
        };

        e.target.classList.add('clicked');

        document.getElementById('archives').classList.add('searching');

        let key = item.dataset.id.toLowerCase();

        let results = posts.filter((post) => {
            return post.tag.includes(key);
        });

        results.forEach((result, i) => {
            let post = document.getElementById(result.id);

            post.parentElement.parentElement.classList.add('hasMatch'); // Optional
            post.classList.add('matched');
        });
    })
})

首先,定义posts为一个数组。接着遍历所有文章,并向数组传入文章的ID和标签。

在这里要注意的是,获取到的标签需要经过JSON.parse转换,因为通过dataset获取到的值是字符串而不是数组。同时,我把所有的标签都转换为小写,避免出现'Javascript'!== 'JavaScript'

接着给所有在#tagCloud元素下的a绑定事件:(cleanUp()这个方法等到最后再说。)

  1. 添加clicked
  2. 通过dataset.id获取标签的ID,并转换成小写
  3. 通过filter()方法来过滤文章
  4. 给筛选出来的文章添加matched类,给父元素添加hasMatch

JS的Filter方法用起来很简单,只要返回true就代表符合要求。我用了includes()来检查是否包含了当前选中了的标签。

检查过后会返回一个新的数组,简单的遍历一下,给符合要求的文章添加matched类即可。

最后,cleanUp()方法是用来删除上一个标签匹配出来的数据。

CSS

#tagCloud a {
    background: #E1E1E1;
    color: #999;
    cursor: pointer;
    padding: 8px 10px;
    display: block;
    margin: 5px 0;
}

#tagCloud a.clicked {
    background: #000;
    color: #fff;
}

#archives.searching .archive-group:not(.hasMatch) {
    display: none;
}

#archives.searching .postList--item:not(.matched) {
    display: none;
}

主要用到了CSS的:not匹配器,把不带.matched类的文章全部隐藏起来。

最终结果

演示:归档 (手机上暂时看不到,被我隐藏了)

简陋的演示:


感觉加上这个后可以把搜索页面给下架了。

Photo by Aleksi Tappura on Unsplash

Comments

edit x send markdown image
paragraph comment heart