search-suggest.js 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. document.addEventListener("DOMContentLoaded", () => {
  2. const inputs = document.querySelectorAll(".search-input");
  3. inputs.forEach((input) => {
  4. const form = input.closest(".search-input-form");
  5. const dropdown = form.querySelector(".search-suggest-dropdown");
  6. let controller = null;
  7. input.addEventListener("input", async () => {
  8. const q = input.value.trim();
  9. if (q.length < 2) {
  10. dropdown.classList.remove("show");
  11. return;
  12. }
  13. // 取消上一次请求(防抖 + 避免竞态)
  14. if (controller) controller.abort();
  15. controller = new AbortController();
  16. try {
  17. const url = new URL(
  18. input.dataset.suggestUrl,
  19. window.location.origin
  20. );
  21. url.searchParams.set("q", q);
  22. url.searchParams.set("limit", 10);
  23. const res = await fetch(url, {
  24. signal: controller.signal,
  25. });
  26. const json = await res.json();
  27. renderSuggestions(json.data.suggestions || []);
  28. } catch (e) {
  29. if (e.name !== "AbortError") {
  30. console.error(e);
  31. }
  32. }
  33. });
  34. function renderSuggestions(list) {
  35. if (!list.length) {
  36. dropdown.classList.remove("show");
  37. return;
  38. }
  39. dropdown.innerHTML = list
  40. .map((item) => {
  41. return `
  42. <button type="button"
  43. class="dropdown-item"
  44. data-text="${item.text}">
  45. <div class="d-flex justify-content-between">
  46. <span>${item.text}</span>
  47. <small class="text-muted">${item.resource_type}</small>
  48. </div>
  49. </button>
  50. `;
  51. })
  52. .join("");
  53. dropdown.classList.add("show");
  54. }
  55. // 点击选择
  56. dropdown.addEventListener("click", (e) => {
  57. const btn = e.target.closest(".dropdown-item");
  58. if (!btn) return;
  59. input.value = btn.dataset.text;
  60. dropdown.classList.remove("show");
  61. form.submit(); // 或者只填充不提交
  62. });
  63. // 失焦隐藏
  64. input.addEventListener("blur", () => {
  65. setTimeout(() => dropdown.classList.remove("show"), 150);
  66. });
  67. input.addEventListener("focus", () => {
  68. if (dropdown.innerHTML.trim()) {
  69. dropdown.classList.add("show");
  70. }
  71. });
  72. });
  73. });