完成系
こんな感じになりました!(CSSで装飾一切なし...;( ˙꒳ ˙ ;):
今回はHTMLで記述をしますが、実際は投稿されている情報を取ってきて、コンテンツを表示してます。
※今回はCMS(WordPress)から情報を取ってくることにしました。
ロジックについて
-
selectボックス
- selectボックスで選択をし、選択したらコンテンツを切り替える
- selectボックスは1番上に「全て」を配置し、「全て」を選択したら全て表示にする
- コンテンツに紐づいている「カテゴリ」で絞り込みが出来るようにする
- 特定のカテゴリを選ばれた状態でページを遷移してくる可能性もあるので、URLフラグメントで対応する
-
もっと見るボタン
- 4件以上の時はもっと見るボタンを活性化する
- もっと見るボタンを押すごとに、4件ずつ表示されるようにする
- 表示する記事がなくなったら非活性にする
まずは簡単にHTMLとCSSを書いていく
html
<select id="select" name="cat" class="js-filter" onchange="location.hash=value">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<div class="js-filter-items">
<div class="list-item js-filter-item" data-cat="1">
<p class="aBusiness-Lineup__label">カテゴリー1のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="3">
<p class="aBusiness-Lineup__label">カテゴリー3のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="3">
<p class="aBusiness-Lineup__label">カテゴリー3のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="2">
<p class="aBusiness-Lineup__label">カテゴリー2のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="1">
<p class="aBusiness-Lineup__label">カテゴリー1のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="1">
<p class="aBusiness-Lineup__label">カテゴリー1のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="2">
<p class="aBusiness-Lineup__label">カテゴリー2のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="1">
<p class="aBusiness-Lineup__label">カテゴリー1のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="1">
<p class="aBusiness-Lineup__label">カテゴリー1のコンテンツ</p>
</div>
<div class="list-item js-filter-item" data-cat="3">
<p class="aBusiness-Lineup__label">カテゴリー3のコンテンツ</p>
</div>
<button id="moreRead" type="button" class="m-Btn">もっと見る</button>
</div>
css
/* select */
.js-filter-item.is-hide {
opacity: 0;
}
.js-filter-item.is-hide-anime {
display: none;
}
.js-filter-item {
transition: opacity 150ms;
opacity: 1;
}
/* more button */
.js-filter-item.hidden {
display: none;
}
JavaScript
JavaScript
let showLengthNum = 4;
let showLength = showLengthNum;
const moreHiddenClass = 'hidden';
const showClass = 'is-show';
const addBtn = document.querySelector('#moreRead');
const targetItem = '.list-item';
const classIsHide = 'is-hide';
const classIsHideAnime = 'is-hide-anime';
//セレクト
const multiFilter = (hideClass, hideClassAnime, target, select = '') => {
const hidden = hideClass;
const hiddenAnime = hideClassAnime;
const targets = document.querySelectorAll(target);
const selects = document.querySelectorAll(select);
const option = document.getElementById('select').options;
if (selects) {
for (let i of selects) {
//ハッシュタグがあったら
if(location.hash) {
//hashと同じselectをselected
for (let j of option) {
j.selected = false;
if(location.hash === '#' + j.value) {
j.selected = true;
}
}
}
//selectを変更した時
i.addEventListener('change', () => {
let hiddenNum = 0;
for (let j of targets) {
j.classList.remove(hidden);
j.classList.remove(moreHiddenClass);
j.classList.add(showClass);
setTimeout(() => {
j.classList.remove(hiddenAnime);
}, 100);
for (let k of selects) {
const value = k.value;
const name = k.getAttribute('name');
const itemData = j.getAttribute('data-' + name);
if (value && value !== 'all' && value !== itemData && !j.classList.contains(hidden)) {
j.classList.add(hidden);
j.classList.remove(showClass);
setTimeout(() => {
j.classList.add(hiddenAnime);
}, 100);
hiddenNum++;
}
}
}
//もっと見るボタンがある場合
if(addBtn) {
showLength = showLengthNum; //再度代入
let hiddenItemTarget = document.querySelectorAll(targetItem + '.' + showClass);
if (hiddenItemTarget.length > showLength) {
for (let i = showLength; i < hiddenItemTarget.length; i++) {
hiddenItemTarget[i].classList.add(moreHiddenClass);
}
}
addBtn.disabled = false;
if (document.querySelectorAll(targetItem + '.' + moreHiddenClass).length == 0) {
addBtn.disabled = true;
}
}
});
}
}
}
multiFilter(classIsHide, classIsHideAnime, '.js-filter-item', '.js-filter');
//もっと見るボタン
const moreBtn = () => {
const targets = document.querySelectorAll('.list-item');
if(addBtn) {
window.addEventListener('DOMContentLoaded', () => {
//hashがある場合
if(location.hash) {
const selects = document.querySelectorAll('.js-filter');
for (let j of targets) {
j.classList.add(classIsHide);
j.classList.add(classIsHideAnime);
j.classList.remove(moreHiddenClass);
for (let k of selects) {
const value = k.value;
const name = k.getAttribute('name');
const itemData = j.getAttribute('data-' + name);
if(location.hash === '#' + itemData) {
j.classList.add(showClass);
j.classList.remove(classIsHide);
j.classList.remove(classIsHideAnime);
}
if(value === 'all') {
j.classList.add(showClass);
j.classList.remove(classIsHide);
j.classList.remove(classIsHideAnime);
}
}
}
let chooseTarget = document.querySelectorAll(targetItem + '.' + showClass);
if (chooseTarget.length > showLength) {
for (let i = showLength; i < chooseTarget.length; i++) {
chooseTarget[i].classList.add(moreHiddenClass);
}
} else {
addBtn.disabled = true;
}
} else {
let targetsLength = targets.length;
if (targetsLength > showLength) {
for (let i = showLength; i < targetsLength; i++) {
targets[i].classList.add(moreHiddenClass);
}
} else {
addBtn.disabled = true;
}
}
});
addBtn.addEventListener('click', () => {
let hiddensTarget = document.querySelectorAll(targetItem + '.' + moreHiddenClass);
if (hiddensTarget.length < showLength) {
showLength = hiddensTarget.length;
}
for (let i = 0; i < showLength; i++) {
hiddensTarget[i].classList.remove(moreHiddenClass);
}
if (document.querySelectorAll(targetItem + '.' + moreHiddenClass).length == 0) {
addBtn.disabled = true;
}
});
}
}
moreBtn();
見た目以上に処理が複雑になってしまった( ˘•_•˘ )
でもJavaScriptだけで実装が出来て良かった!
時間がある時に改修できたらいいなー
次はTypeScriptで何か作ってみる(✧ω✧)