AniList Status Filter

Filter anime by your status on AniList search page.

当前为 2023-09-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AniList Status Filter
  3. // @namespace https://github.com/SlashNephy
  4. // @version 0.1.2
  5. // @author SlashNephy
  6. // @description Filter anime by your status on AniList search page.
  7. // @description:ja AniListの作品検索ページ内で自分の視聴ステータスでフィルターできるようにします。
  8. // @homepage https://scrapbox.io/slashnephy/AniList_%E3%81%A7%E8%87%AA%E5%88%86%E3%81%AE%E8%A6%96%E8%81%B4%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%81%AB%E5%BF%9C%E3%81%98%E3%81%A6%E4%BD%9C%E5%93%81%E3%82%92%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC%E3%81%99%E3%82%8B_UserScript
  9. // @homepageURL https://scrapbox.io/slashnephy/AniList_%E3%81%A7%E8%87%AA%E5%88%86%E3%81%AE%E8%A6%96%E8%81%B4%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%81%AB%E5%BF%9C%E3%81%98%E3%81%A6%E4%BD%9C%E5%93%81%E3%82%92%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC%E3%81%99%E3%82%8B_UserScript
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=anilist.co
  11. // @supportURL https://github.com/SlashNephy/userscripts/issues
  12. // @match https://anilist.co/*
  13. // @grant none
  14. // @license MIT license
  15. // ==/UserScript==
  16.  
  17. (function () {
  18. 'use strict';
  19.  
  20. const style = document.createElement('style');
  21. document.head.appendChild(style);
  22. const hiddenStatuses = {
  23. Watching: false,
  24. Reading: false,
  25. Completed: false,
  26. Planning: false,
  27. Paused: false,
  28. Dropped: false,
  29. };
  30. const renderCheckbox = (title, onClick) => {
  31. const box = document.createElement('div');
  32. box.classList.add('filter', 'checkbox-wrap');
  33. box.toggleAttribute('data-v-acf5fe42');
  34. {
  35. const wrapper = document.createElement('div');
  36. wrapper.classList.add('checkbox-wrap');
  37. wrapper.toggleAttribute('data-v-acf5fe42');
  38. wrapper.toggleAttribute('data-v-32107ecb');
  39. wrapper.addEventListener('click', onClick);
  40. box.appendChild(wrapper);
  41. {
  42. const checkbox = document.createElement('div');
  43. checkbox.classList.add('checkbox');
  44. checkbox.toggleAttribute('data-v-32107ecb');
  45. wrapper.appendChild(checkbox);
  46. {
  47. const check = document.createElement('div');
  48. check.classList.add('check');
  49. check.toggleAttribute('data-v-32107ecb');
  50. checkbox.appendChild(check);
  51. }
  52. }
  53. {
  54. const label = document.createElement('div');
  55. label.classList.add('label');
  56. label.toggleAttribute('data-v-32107ecb');
  57. label.textContent = title;
  58. wrapper.appendChild(label);
  59. }
  60. }
  61. return box;
  62. };
  63. const renderCss = () => {
  64. const statuses = Object.entries(hiddenStatuses)
  65. .filter(([_, hide]) => hide)
  66. .map(([key, _]) => key);
  67. if (statuses.length === 0) {
  68. return '';
  69. }
  70. const selectors = statuses.map((s) => `.media-card:has(> a div[status="${s}"])`).join(',');
  71. return `${selectors} { display: none; }`;
  72. };
  73. const renderFilters = (children) => {
  74. const filters = document.createElement('div');
  75. {
  76. const name = document.createElement('div');
  77. name.classList.add('name');
  78. name.toggleAttribute('data-v-84c4e64c');
  79. name.textContent = 'my status';
  80. filters.appendChild(name);
  81. }
  82. {
  83. const wrapper = document.createElement('div');
  84. wrapper.classList.add('filters-wrap', 'checkbox');
  85. wrapper.toggleAttribute('data-v-acf5fe42');
  86. wrapper.append(...children);
  87. filters.appendChild(wrapper);
  88. }
  89. return filters;
  90. };
  91. const toggleCheckbox = (e, key) => {
  92. hiddenStatuses[key] = !hiddenStatuses[key];
  93. style.textContent = renderCss();
  94. const element = e.currentTarget;
  95. const check = element?.querySelector('.check');
  96. if (check === null || check === undefined) {
  97. return;
  98. }
  99. check.style.display = hiddenStatuses[key] ? 'none' : 'initial';
  100. };
  101. const detectCategory = () => {
  102. if (window.location.pathname.startsWith('/search/anime')) {
  103. return 'anime';
  104. }
  105. if (window.location.pathname.startsWith('/search/manga')) {
  106. return 'manga';
  107. }
  108. return null;
  109. };
  110. const attach = () => {
  111. const category = detectCategory();
  112. if (category === null) {
  113. return;
  114. }
  115. const extraFiltersWrap = document.querySelector('.extra-filters-wrap');
  116. const attribute = 'anilist-status-filter-attached';
  117. if (extraFiltersWrap === null || extraFiltersWrap.hasAttribute(attribute)) {
  118. return;
  119. }
  120. extraFiltersWrap.insertAdjacentElement('afterend', renderFilters([
  121. renderCheckbox(category === 'anime' ? 'Watching' : 'Reading', (e) => {
  122. switch (detectCategory()) {
  123. case 'anime':
  124. toggleCheckbox(e, 'Watching');
  125. return;
  126. case 'manga':
  127. toggleCheckbox(e, 'Reading');
  128. }
  129. }),
  130. renderCheckbox('Completed', (e) => {
  131. toggleCheckbox(e, 'Completed');
  132. }),
  133. renderCheckbox('Planning', (e) => {
  134. toggleCheckbox(e, 'Planning');
  135. }),
  136. renderCheckbox('Paused', (e) => {
  137. toggleCheckbox(e, 'Paused');
  138. }),
  139. renderCheckbox('Dropped', (e) => {
  140. toggleCheckbox(e, 'Dropped');
  141. }),
  142. ]));
  143. extraFiltersWrap.toggleAttribute(attribute);
  144. };
  145. window.addEventListener('load', attach);
  146. window.addEventListener('click', attach);
  147.  
  148. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址