style: 演唱會搜尋說明
This commit is contained in:
+31
-3
@@ -103,6 +103,17 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.concert-search-hint {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 720px;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
padding: 0 6px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: rgba(255, 255, 255, 0.78);
|
||||||
|
}
|
||||||
|
|
||||||
.concert-search-summary {
|
.concert-search-summary {
|
||||||
margin: 0 0 12px;
|
margin: 0 0 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -130,6 +141,18 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.concert-search-hits li.concert-search-hit-jump {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease, border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.concert-search-hits li.concert-search-hit-jump:hover,
|
||||||
|
.concert-search-hits li.concert-search-hit-jump:focus-visible {
|
||||||
|
background: rgba(255, 204, 0, 0.15);
|
||||||
|
border-color: #ffcc00;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.concert-search-hits .hit-track {
|
.concert-search-hits .hit-track {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -171,12 +194,17 @@
|
|||||||
<div class="search-section">
|
<div class="search-section">
|
||||||
<div class="concert-search-box">
|
<div class="concert-search-box">
|
||||||
<div class="concert-search-row">
|
<div class="concert-search-row">
|
||||||
<input type="text" class="search-input" id="concert-query" value="" placeholder="搜尋曲目或場次">
|
<input type="text" class="search-input" id="concert-query" value="" placeholder="搜尋曲目或場次"
|
||||||
<input type="text" class="search-input" id="concert-member" value="" placeholder="成員或段落(可空白)">
|
aria-describedby="concert-search-hint">
|
||||||
|
<input type="text" class="search-input" id="concert-member" value=""
|
||||||
|
placeholder="成員或 ENCORE/Solo…(可空白)" aria-describedby="concert-search-hint">
|
||||||
<button type="button" class="concert-search-btn" id="concert-search-btn">查詢</button>
|
<button type="button" class="concert-search-btn" id="concert-search-btn">查詢</button>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="concert-search-hint" id="concert-search-hint">
|
||||||
|
<strong>曲目或場次</strong>:曲名、巡演名或場地(例:COUPLE、釜山、YELLOW NOTE)。<strong>成員或段落</strong>:可留白;要縮小範圍時可填成員名,或歌單裡的段落標記(例:殷志源、ENCORE、BLACKKIES、Solo)。兩格都有填時,需同時符合。按查詢或 Enter;點結果可跳到下方歌單。
|
||||||
|
</p>
|
||||||
<div id="concert-search-result" class="concert-search-result" aria-live="polite">
|
<div id="concert-search-result" class="concert-search-result" aria-live="polite">
|
||||||
<p class="concert-search-summary">輸入後按「查詢」,會在下方列出比對到的曲目。</p>
|
<p class="concert-search-summary">尚未查詢。</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+81
-23
@@ -43,6 +43,36 @@ function clearSearchHighlights() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expandVenueForTrackItem(trackItem) {
|
||||||
|
const venueBlock = trackItem?.closest(".track-list")?.parentElement;
|
||||||
|
const expand = venueBlock?.querySelector(".expand-trigger");
|
||||||
|
if (expand) {
|
||||||
|
expand.checked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToSearchResults() {
|
||||||
|
if (!myResult) return;
|
||||||
|
myResult.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpToTrackHit(hit) {
|
||||||
|
if (!hit?.el) return;
|
||||||
|
|
||||||
|
clearSearchHighlights();
|
||||||
|
expandVenueForTrackItem(hit.el);
|
||||||
|
hit.el.classList.add("is-search-hit");
|
||||||
|
hit.el.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveSearchListItem(list, activeLi) {
|
||||||
|
if (!list) return;
|
||||||
|
list.querySelectorAll(".concert-search-hit-jump").forEach(function (item) {
|
||||||
|
item.classList.remove("is-active");
|
||||||
|
});
|
||||||
|
if (activeLi) activeLi.classList.add("is-active");
|
||||||
|
}
|
||||||
|
|
||||||
function renderSearchResults(getQuery, getMember, hits) {
|
function renderSearchResults(getQuery, getMember, hits) {
|
||||||
if (!myResult) return;
|
if (!myResult) return;
|
||||||
|
|
||||||
@@ -52,15 +82,15 @@ function renderSearchResults(getQuery, getMember, hits) {
|
|||||||
summary.className = "concert-search-summary";
|
summary.className = "concert-search-summary";
|
||||||
|
|
||||||
if (!hits.length) {
|
if (!hits.length) {
|
||||||
summary.textContent = `關鍵字「${getQuery || "(未填)"}」${getMember.trim() ? `、成員「${getMember}」` : ""}:沒有比對到曲目,請換字再試。`;
|
summary.textContent = `曲目或場次「${getQuery || "(未填)"}」${getMember.trim() ? `、成員「${getMember}」` : ""}:沒有比對到曲目,請換字再試。`;
|
||||||
myResult.replaceChildren(summary);
|
myResult.replaceChildren(summary);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getMember.trim()) {
|
if (getMember.trim()) {
|
||||||
summary.textContent = `關鍵字「${getQuery || "(未填)"}」、成員「${getMember}」:共 ${hits.length} 筆,以下列出前 ${Math.min(hits.length, SEARCH_LIST_MAX)} 筆。`;
|
summary.textContent = `曲目或場次「${getQuery || "(未填)"}」、成員「${getMember}」:共 ${hits.length} 筆。點任一列可跳到歌單。`;
|
||||||
} else {
|
} else {
|
||||||
summary.textContent = `關鍵字「${getQuery}」:共 ${hits.length} 筆,以下列出前 ${Math.min(hits.length, SEARCH_LIST_MAX)} 筆。`;
|
summary.textContent = `曲目或場次「${getQuery}」:共 ${hits.length} 筆。點任一列可跳到歌單。`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = document.createElement("ul");
|
const list = document.createElement("ul");
|
||||||
@@ -70,6 +100,11 @@ function renderSearchResults(getQuery, getMember, hits) {
|
|||||||
if (hit.el) hit.el.classList.add("is-search-hit");
|
if (hit.el) hit.el.classList.add("is-search-hit");
|
||||||
|
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
|
li.className = "concert-search-hit-jump";
|
||||||
|
li.setAttribute("role", "button");
|
||||||
|
li.setAttribute("tabindex", "0");
|
||||||
|
li.setAttribute("aria-label", `跳到曲目:${hit.trackName}`);
|
||||||
|
|
||||||
const trackLine = document.createElement("span");
|
const trackLine = document.createElement("span");
|
||||||
trackLine.className = "hit-track";
|
trackLine.className = "hit-track";
|
||||||
trackLine.textContent = hit.trackName;
|
trackLine.textContent = hit.trackName;
|
||||||
@@ -80,41 +115,64 @@ function renderSearchResults(getQuery, getMember, hits) {
|
|||||||
|
|
||||||
li.appendChild(trackLine);
|
li.appendChild(trackLine);
|
||||||
if (metaLine.textContent) li.appendChild(metaLine);
|
if (metaLine.textContent) li.appendChild(metaLine);
|
||||||
|
|
||||||
|
li.addEventListener("click", function () {
|
||||||
|
jumpToTrackHit(hit);
|
||||||
|
});
|
||||||
|
li.addEventListener("keydown", function (event) {
|
||||||
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
|
event.preventDefault();
|
||||||
|
jumpToTrackHit(hit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
list.appendChild(li);
|
list.appendChild(li);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hits.length > SEARCH_LIST_MAX) {
|
if (hits.length > SEARCH_LIST_MAX) {
|
||||||
const more = document.createElement("li");
|
const more = document.createElement("li");
|
||||||
more.className = "hit-more";
|
more.className = "hit-more";
|
||||||
more.textContent = `另有 ${hits.length - SEARCH_LIST_MAX} 筆未顯示,請縮小關鍵字。`;
|
more.textContent = `另有 ${hits.length - SEARCH_LIST_MAX} 筆未顯示,請縮小曲目或場次。`;
|
||||||
list.appendChild(more);
|
list.appendChild(more);
|
||||||
}
|
}
|
||||||
|
|
||||||
myResult.replaceChildren(summary, list);
|
myResult.replaceChildren(summary, list);
|
||||||
|
|
||||||
const firstHit = hits[0]?.el;
|
jumpToTrackHit(hits[0]);
|
||||||
if (firstHit) {
|
}
|
||||||
firstHit.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
||||||
|
function runSearch() {
|
||||||
|
let getQuery = myQuery ? myQuery.value : "";
|
||||||
|
let getMember = myMember ? myMember.value : "";
|
||||||
|
|
||||||
|
if (!getQuery.trim() && !getMember.trim()) {
|
||||||
|
myResult.replaceChildren();
|
||||||
|
const summary = document.createElement("p");
|
||||||
|
summary.className = "concert-search-summary";
|
||||||
|
summary.textContent = "請至少輸入曲目/場次,或成員其中一項。";
|
||||||
|
myResult.appendChild(summary);
|
||||||
|
clearSearchHighlights();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hits = findMatchingTracks(getQuery, getMember);
|
||||||
|
renderSearchResults(getQuery, getMember, hits);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearchEnter(event) {
|
||||||
|
if (event.key !== "Enter") return;
|
||||||
|
event.preventDefault();
|
||||||
|
runSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.action
|
// 2.action
|
||||||
if (myBtn && myResult) {
|
if (myBtn && myResult) {
|
||||||
myBtn.addEventListener("click", function () {
|
myBtn.addEventListener("click", runSearch);
|
||||||
let getQuery = myQuery ? myQuery.value : "";
|
|
||||||
let getMember = myMember ? myMember.value : "";
|
|
||||||
|
|
||||||
if (!getQuery.trim() && !getMember.trim()) {
|
if (myQuery) {
|
||||||
myResult.replaceChildren();
|
myQuery.addEventListener("keydown", handleSearchEnter);
|
||||||
const summary = document.createElement("p");
|
}
|
||||||
summary.className = "concert-search-summary";
|
if (myMember) {
|
||||||
summary.textContent = "請至少輸入曲目/場次,或成員其中一項。";
|
myMember.addEventListener("keydown", handleSearchEnter);
|
||||||
myResult.appendChild(summary);
|
}
|
||||||
clearSearchHighlights();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hits = findMatchingTracks(getQuery, getMember);
|
|
||||||
renderSearchResults(getQuery, getMember, hits);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user