Greasy Fork

Greasy Fork is available in English.

DLsite Search Form at Top (Move)

左カラムの検索フォームを複製せずに上部へ「移動」させ、追従させます。SPA更新にも追従します。

当前为 2025-08-11 提交的版本,查看 最新版本

// ==UserScript==
// @name         DLsite Search Form at Top (Move)
// @namespace    https://x.com/kawaiiinf
// @version      1.4
// @description  左カラムの検索フォームを複製せずに上部へ「移動」させ、追従させます。SPA更新にも追従します。
// @author       Kawaii monkey
// @license      BSD 2-Clause
// @match        https://www.dlsite.com/*
// @grant        GM_addStyle
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  // ===== 設定 =====
  const CONTAINER_ID = 'dlst-search-top-container';
  const MOVED_ATTR = 'data-dlst-moved';
  const DEBUG = false; // trueでログ

  function log(...args) { if (DEBUG) console.log('[DLsite Search Top v1.4]', ...args); }

  function addStyle(css) {
    if (typeof GM_addStyle === 'function') GM_addStyle(css);
    else {
      const s = document.createElement('style');
      s.textContent = css; document.head.appendChild(s);
    }
  }

  addStyle(`
    #${CONTAINER_ID} { position: sticky; top: 0; z-index: 1000; background: rgba(250,250,250,.85); backdrop-filter: saturate(1.05) blur(3px); border-bottom: 1px solid rgba(0,0,0,.06); padding: 8px 10px; }
    #${CONTAINER_ID} form { margin: 0; }
    #${CONTAINER_ID} input[type="search"] { width: calc(100% - 30px) !important; }
  `);

  function getTargets() {
    const cpSearch = document.querySelector('.cp_search');
    const searchTop = document.querySelector('.search_top');
    if (!cpSearch || !searchTop) return null;
    const form = cpSearch.closest('form');
    if (!form) return null;
    return { cpSearch, form, searchTop };
  }

  function ensureContainer(searchTop) {
    let container = document.getElementById(CONTAINER_ID);
    if (!container) {
      container = document.createElement('div');
      container.id = CONTAINER_ID;
      searchTop.after(container);
      log('Container inserted after .search_top');
    }
    return container;
  }

  function moveFormToTop() {
    const t = getTargets();
    if (!t) return;
    const { form, searchTop } = t;

    // すでに移動済みならスキップ
    if (form.getAttribute(MOVED_ATTR) === '1' && form.parentElement && form.parentElement.id === CONTAINER_ID) {
      return;
    }

    const container = ensureContainer(searchTop);

    // 移動(複製しない):イベントや挙動はそのまま維持されます
    container.appendChild(form);
    form.setAttribute(MOVED_ATTR, '1');
    log('Form moved to top container');
  }

  function bootstrap() {
    moveFormToTop();

    // SPA更新対応:DOM変化を監視し、フォームが再出現したら再移動
    let timer = 0;
    const obs = new MutationObserver(() => {
      clearTimeout(timer);
      timer = setTimeout(moveFormToTop, 60);
    });
    obs.observe(document.documentElement, { childList: true, subtree: true });

    window.addEventListener('pageshow', moveFormToTop, { once: true });
    window.addEventListener('popstate', () => setTimeout(moveFormToTop, 50));
    document.addEventListener('turbo:load', moveFormToTop);
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bootstrap);
  else bootstrap();
})();