Greasy Fork

Greasy Fork is available in English.

AliExpress Product Link Fixer

Enhance your AliExpress shopping experience by converting marketing links into direct product links, ensuring each product is easily accessible with a single click.

目前为 2025-01-11 提交的版本,查看 最新版本

// ==UserScript==
// @name         AliExpress Product Link Fixer
// @namespace    http://tampermonkey.net/
// @version      2.0
// @license      MIT
// @description  Enhance your AliExpress shopping experience by converting marketing links into direct product links, ensuring each product is easily accessible with a single click.
// @author       NewsGuyTor
// @match        https://*.aliexpress.com/*
// @icon         https://www.aliexpress.com/favicon.ico
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Initialize the MutationObserver
    let observer;

    // Main function to fix links
    function fixLinks() {
        // Temporarily disconnect the observer to prevent infinite loops
        if (observer) observer.disconnect();

        try {
            removeMarketingAnchors();
            rewriteAnchorsWithProductIds();
            fixOrCreateLinksForDataProducts();
        } catch (err) {
            console.error("[AliExpress Product Link Fixer v2] Error in fixLinks():", err);
        }

        // Reconnect the observer after making changes
        if (observer) observer.observe(document.body, { childList: true, subtree: true });
    }

    /**
     * A) Remove "marketing" anchors that have /gcp/ or /ssr/ without ?productIds=...
     *    Then unwrap them to expose individual product blocks
     */
    function removeMarketingAnchors() {
        const anchors = document.querySelectorAll('a[href*="/gcp/"]:not([data-alifix-done]), a[href*="/ssr/"]:not([data-alifix-done])');

        anchors.forEach(a => {
            if (a.dataset.alifixDone) return; // Skip already processed anchors

            const url = new URL(a.href);
            if (!url.searchParams.has('productIds')) {
                // Mark the anchor as processed
                a.dataset.alifixDone = "1";
                // Remove the anchor but keep its children in the DOM
                unwrapAnchor(a);
            }
        });
    }

    /**
     * B) Rewrite anchors that contain ?productIds=... to direct product pages
     */
    function rewriteAnchorsWithProductIds() {
        const anchors = document.querySelectorAll('a[href*="/gcp/"]:not([data-alifix-done]), a[href*="/ssr/"]:not([data-alifix-done])');

        anchors.forEach(a => {
            if (a.dataset.alifixDone) return; // Skip already processed anchors

            const url = new URL(a.href);
            const pid = url.searchParams.get('productIds');
            if (pid) {
                a.href = `https://${url.host}/item/${pid}.html`;
                a.dataset.alifixDone = "1"; // Mark as processed
            }
        });
    }

    /**
     * C) Ensure each <div data-product-ids="..."> has a clickable link
     *    Either by fixing existing anchors or creating new ones
     */
    function fixOrCreateLinksForDataProducts() {
        const divs = document.querySelectorAll('[data-product-ids]:not([data-alifix-done])');

        divs.forEach(div => {
            // Mark the div as processed
            div.dataset.alifixDone = "1";

            const pid = div.dataset.productIds;
            if (!pid) return;

            // Check if there's already an <a> inside the div
            const existingAnchor = div.querySelector('a[href]');
            if (existingAnchor) {
                if (!existingAnchor.dataset.alifixDone) {
                    existingAnchor.href = `https://${location.host}/item/${pid}.html`;
                    existingAnchor.dataset.alifixDone = "1"; // Mark as processed
                }
            } else {
                // No anchor found, create one around the div
                const link = document.createElement('a');
                link.href = `https://${location.host}/item/${pid}.html`;
                link.dataset.alifixDone = "1";
                // Optionally, style the link to display as block to ensure full area is clickable
                link.style.display = "block";
                // Insert the link before the div
                div.parentNode.insertBefore(link, div);
                // Move the div inside the new link
                link.appendChild(div);
            }
        });
    }

    /**
     * Helper function to unwrap an anchor tag but keep its child elements
     * @param {HTMLElement} anchor - The anchor element to unwrap
     */
    function unwrapAnchor(anchor) {
        const parent = anchor.parentNode;
        if (!parent) return;
        while (anchor.firstChild) {
            parent.insertBefore(anchor.firstChild, anchor);
        }
        parent.removeChild(anchor);
    }

    // Initial execution on page load
    fixLinks();

    // Set up the MutationObserver to watch for dynamic content changes
    observer = new MutationObserver(fixLinks);
    observer.observe(document.body, { childList: true, subtree: true });

})();