Greasy Fork

Greasy Fork is available in English.

抖音免登录搜索跳转

拦截抖音搜索页面的点击事件,将关键词拼接到搜索URL[/root/search/${keyword}?source=pc_click_hashtag_feed, /jingxuan/search/${keyword}?source=comment_related_search]并跳转

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         抖音免登录搜索跳转
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  拦截抖音搜索页面的点击事件,将关键词拼接到搜索URL[/root/search/${keyword}?source=pc_click_hashtag_feed, /jingxuan/search/${keyword}?source=comment_related_search]并跳转
// @author       SHANGDISHIGE109
// @source       https://github.com/SHANGDISHIGE109/douyin-notlogin-search
// @supportURL   https://github.com/SHANGDISHIGE109/douyin-notlogin-search/issues
// @license      MIT
// @match        https://www.douyin.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=douyin.com
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 搜索按钮
    function getSearchButton() {
        return document.querySelector('button[data-e2e="searchbar-button"]');
    }

    // 搜索输入框
    function getSearchInput() {
        return document.querySelector('input[data-e2e="searchbar-input"]');
    }

    // 历史记录
    function getSearchHistoryList() {
        let container = document.querySelector('div[data-e2e="search-history-container"]');
        if (!container) return [];
        return Array.from(container.children);
    }

    // 猜你想搜
    function getSearchGuessList() {
        let container = document.querySelector('div[data-e2e="search-guess-container"]');
        if (!container) return [];
        return Array.from(container.children);
    }

    // 抖音热点
    function getSearchHotList() {
        let container = document.querySelector('div[data-e2e="search-hot-container"]');
        if (!container) return [];
        return Array.from(container.children);
    }

    // 搜索框输入联想建议
    function getSearchSuggestList() {
        return Array.from(document.querySelectorAll('div[data-isrichsug="0"]'));
    }

    // 获取文本内容的通用函数
    function getTextFromElement(element) {
        return element.textContent.trim();
    }

    // 统一的事件处理函数(避免重复创建函数实例)
    const handleSearchItemClick = function(event) {
        event.preventDefault();
        event.stopPropagation();
        const keyword = getTextFromElement(this);
        redirectToSearch(keyword);
    };

    const handleSearchBtnClick = function(event) {
        event.preventDefault();
        event.stopPropagation();
        const searchInput = getSearchInput();
        const inputText = searchInput ? searchInput.value.trim() : '';
        inputText && redirectToSearch(inputText);
    };

    const handleInputEnter = function(event) {
        if (event.key === 'Enter' || event.keyCode === 13) {
            event.preventDefault();
            event.stopImmediatePropagation();
            const searchInput = getSearchInput();
            const inputText = searchInput ? searchInput.value.trim() : '';
            inputText && redirectToSearch(inputText);
            return false;
        }
    };

    // 清理已有事件监听(关键:防止重复绑定)
    function clearExistingListeners() {
        const searchInput = getSearchInput();
        searchInput.removeEventListener('keydown', handleInputEnter);

        const searchBtn = getSearchButton();
        searchBtn.removeEventListener('click', handleSearchBtnClick);
    }

    function clearExistingListenersDynamic() {
        // 清理各类搜索项的点击事件
        const allItems = [
            ...getSearchHistoryList(),
            ...getSearchGuessList(),
            ...getSearchHotList(),
            ...getSearchSuggestList(),
            ...oldDynamicElements
        ];
        allItems.forEach(item => item.removeEventListener('click', handleSearchItemClick));
        oldDynamicElements.length = 0;
    }

    // 拦截元素点击事件的函数
    function interceptClickEvents() {
        // 先清理旧监听,再绑定新监听
        clearExistingListeners();
        // 拦截搜索按钮点击
        getSearchButton().addEventListener('click', handleSearchBtnClick, { once: false, passive: false });
        // 拦截搜索输入框回车事件
        getSearchInput().addEventListener('keydown', handleInputEnter, { once: false, passive: false });

        interceptClickEventsDynamic();
    }
    
    function interceptClickEventsDynamic() {
        // 这几个list是动态插入到DOM中的,需要在父容器上使用事件委托来监听点击事件,避免频繁绑定和解绑
        globalObserver = new MutationObserver(function(mutations) {
            let hasNewNodes = mutations.some(mut => mut.type === 'childList' && mut.addedNodes.length > 0);
            if (hasNewNodes) {
                // 延长一点时间,确保DOM完全渲染,减少无效执行
                setTimeout(() => {
                    clearExistingListenersDynamic();
                    // 拦截历史记录/猜你想搜/热点项点击(优化:事件委托替代逐个绑定)
                    const bindItemEvents = (listGetter) => {
                        const items = listGetter();
                        items.forEach(item => {
                            item.addEventListener('click', handleSearchItemClick, { once: false, passive: false });
                        });
                        oldDynamicElements.push(...items);
                    };
                    bindItemEvents(getSearchHistoryList);
                    bindItemEvents(getSearchGuessList);
                    bindItemEvents(getSearchHotList);
                    bindItemEvents(getSearchSuggestList);
                }, 80);
            }
        });
        globalObserver.observe(getSearchButton().parentNode, {
            childList: true,
            subtree: true,
        });
    }

    // 执行重定向到搜索结果页
    function redirectToSearch(keyword) {
        if (keyword && keyword.trim()) {
            const encodedKeyword = encodeURIComponent(keyword.trim());
            const targetUrl = `https://www.douyin.com/root/search/${keyword}?source=comment_related_search`;
            window.location.href = targetUrl;
        } else {
            console.log("关键词为空,无法跳转");
        }
    }
    
    // 初始化函数
    function initializeSearchInterceptor() {
        // 确保脚本的绑定事件在页面原生事件之后执行
        const hasNativeClick = getSearchButton().onclick;
        if (hasNativeClick) {
            interceptClickEvents();
            console.log('搜索拦截器已初始化');
        }else {
            const intervalId = setInterval(() => {
                const hasNativeClick = getSearchButton().onclick;
                if (hasNativeClick) {
                    clearInterval(intervalId);
                    interceptClickEvents();
                    console.log('搜索拦截器已初始化');
                }
            }, 2000);
            intervalIds.push(intervalId);
        }
    }
    
    // 检查并初始化搜索拦截器
    function checkAndInitialize() {
        // 检查搜索按钮和搜索输入框是否存在
        if (getSearchButton() && getSearchInput()) {
            // 元素存在,直接初始化
            initializeSearchInterceptor();
        } else {
            // 如果没有找到搜索按钮或搜索输入框,则设置定时器每两秒检查一次
            const intervalId = setInterval(function() {
                if (getSearchButton() && getSearchInput()) {
                    clearInterval(intervalId);
                    // 元素已加载,执行初始化
                    initializeSearchInterceptor();
                }
            }, 2000);
            intervalIds.push(intervalId);
        }
    }

    // 销毁所有观察者和定时器
    function destroyResources() {
        // 销毁全局DOM观察者
        if (globalObserver) {
            globalObserver.disconnect();
            globalObserver = null;
        }
        // 清理所有定时器
        intervalIds.forEach(id => clearInterval(id));
        intervalIds = [];
        // 清理事件监听
        clearExistingListeners();
        clearExistingListenersDynamic();
        console.log('已销毁所有资源');
    }

    // 只对非登录用户生效
    if (document.querySelectorAll('.semi-button.semi-button-primary').length == 0) {
        return;
    }

    let globalObserver = null;
    let intervalIds = [];
    let oldDynamicElements = [];

    // 页面卸载时清理所有资源
    window.addEventListener('beforeunload', destroyResources);
    // 检查文档加载状态并相应处理
    if (document.readyState === 'loading') {
        // 文档仍在加载中,监听 DOMContentLoaded 事件
        document.addEventListener('DOMContentLoaded', function() {
            checkAndInitialize();
        });
    } else {
        // 文档已加载完成,直接执行初始化
        checkAndInitialize();
    }
})();