Greasy Fork

Greasy Fork is available in English.

禁用电商平台图片缩放效果

禁用淘宝、天猫、1688等电商平台的js-image-zoom效果,方便右键保存图片。修复动态类名问题

当前为 2025-05-28 提交的版本,查看 最新版本

// ==UserScript==
// @name             禁用电商平台图片缩放效果
// @name:en          Disable E-commerce Image Zoom Effect
// @namespace        http://greasyfork.icu/users/3001-hanjian-wu
// @version          1.3.3
// @description      禁用淘宝、天猫、1688等电商平台的js-image-zoom效果,方便右键保存图片。修复动态类名问题
// @description:en   Disable js-image-zoom effect on Taobao, Tmall, 1688 and other e-commerce platforms for easy right-click image saving. Fixed dynamic class names issue
// @author           hanjian wu
// @homepage         http://greasyfork.icu/users/3001-hanjian-wu
// @supportURL       http://greasyfork.icu/users/3001-hanjian-wu
// @license          MIT
// @match            https://*.taobao.com/item.htm*
// @match            https://*.tmall.com/item.htm*
// @match            https://*.1688.com/offer/*.html*
// @match            https://item.taobao.com/*
// @match            https://detail.tmall.com/*
// @match            https://detail.1688.com/*
// @icon             data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiByeD0iOCIgZmlsbD0iIzAwN0FGRiIvPgo8cGF0aCBkPSJNMTYgMTZIMzJWMzJIMTZWMTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzYgMTZINDhWMjhIMzZWMTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTYgMzZIMjhWNDhIMTZWMzZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzIgMzJINDhWNDhIMzJWMzJaIiBmaWxsPSJ3aGl0ZSIgZmlsbC1vcGFjaXR5PSIwLjciLz4KPC9zdmc+
// @grant            none
// @run-at           document-start
// @noframes
// @compatible       chrome
// @compatible       firefox
// @compatible       edge
// @compatible       safari
// ==/UserScript==

(function() {
    'use strict';

    console.log('电商图片缩放禁用脚本已启动 v1.3.3 (动态类名修复版)');

    // 检测当前平台
    const is1688 = window.location.hostname.includes('1688.com');
    const isTaobaoTmallCommon = window.location.hostname.includes('taobao.com') || window.location.hostname.includes('tmall.com');

    let zoomEventBlocked = false;
    let rightClickFixed = false;

    // 通用的类名匹配函数
    function hasClassContaining(element, substring) {
        if (!element.classList) return false;
        return Array.from(element.classList).some(cls => cls.includes(substring));
    }

    // 查找包含特定子串的元素
    function findElementsWithClassContaining(substring, parent = document) {
        const elements = [];
        const allElements = parent.querySelectorAll('*');
        allElements.forEach(el => {
            if (hasClassContaining(el, substring)) {
                elements.push(el);
            }
        });
        return elements;
    }

    // 禁用图片缩放的主要函数
    function disableImageZoom() {
        if (is1688) {
            handle1688Zoom();
        } else if (isTaobaoTmallCommon) {
            handleTaobaoTmallZoom();
        }
    }

    // 处理1688的缩放
    function handle1688Zoom() {
        const zoomElements = document.querySelectorAll('.scale-img, .scaled-img');
        zoomElements.forEach(el => {
            if (el.style.display !== 'none') {
                el.style.setProperty('display', 'none', 'important');
                el.style.setProperty('visibility', 'hidden', 'important');
                el.style.setProperty('opacity', '0', 'important');
                el.style.setProperty('pointer-events', 'none', 'important');
            }
        });
        const previewImg = document.querySelector('.detail-gallery-preview .preview-img');
        if (previewImg) {
            enableImageRightClick(previewImg);
        }
    }

    // 处理淘宝/天猫的缩放 - 使用动态类名匹配
    function handleTaobaoTmallZoom() {
        // 隐藏缩放相关元素
        const zoomElements = document.querySelectorAll('.js-image-zoom__zoomed-area, .js-image-zoom__zoomed-image, #lensDiv');
        zoomElements.forEach(el => {
            el.style.setProperty('display', 'none', 'important');
            el.style.setProperty('visibility', 'hidden', 'important');
            el.style.setProperty('opacity', '0', 'important');
            el.style.setProperty('pointer-events', 'none', 'important');
        });

        // 使用动态类名查找主图片容器
        const mainPicWrappers = findElementsWithClassContaining('--mainPicWrap--');
        console.log('找到主图片容器数量:', mainPicWrappers.length);
        
        mainPicWrappers.forEach(wrapper => {
            // 让容器事件穿透
            wrapper.style.setProperty('pointer-events', 'none', 'important');
            console.log('容器事件已设置为穿透:', wrapper.className);
            
            // 查找容器内的主图片
            const mainPics = Array.from(wrapper.querySelectorAll('img')).filter(img => 
                hasClassContaining(img, '--mainPic--')
            );
            
            console.log('在容器中找到主图片数量:', mainPics.length);
            
            mainPics.forEach(mainPic => {
                // 恢复图片的鼠标事件
                mainPic.style.setProperty('pointer-events', 'auto', 'important');
                mainPic.style.setProperty('position', 'relative', 'important');
                mainPic.style.setProperty('z-index', '9999', 'important');
                enableImageRightClick(mainPic);
                console.log('已修复图片右键功能:', mainPic.src);
            });
        });

        // 确保其他交互元素正常工作
        fixOtherInteractiveElements();
    }

    // 修复其他交互元素
    function fixOtherInteractiveElements() {
        // 缩略图
        const thumbnails = findElementsWithClassContaining('--thumbnail--');
        thumbnails.forEach(thumb => {
            thumb.style.setProperty('pointer-events', 'auto', 'important');
            const thumbImgs = Array.from(thumb.querySelectorAll('img')).filter(img => 
                hasClassContaining(img, '--thumbnailPic--')
            );
            thumbImgs.forEach(thumbImg => {
                thumbImg.style.setProperty('pointer-events', 'auto', 'important');
            });
        });

        // 切换标签
        const switchTabs = findElementsWithClassContaining('--switchTabsItem--');
        switchTabs.forEach(tab => {
            tab.style.setProperty('pointer-events', 'auto', 'important');
        });

        // 视频元素
        const videoContainers = document.querySelectorAll('.videox-container');
        videoContainers.forEach(container => {
            container.style.setProperty('pointer-events', 'auto', 'important');
            const video = container.querySelector('video');
            if (video) {
                video.style.setProperty('pointer-events', 'auto', 'important');
            }
        });
    }

    // 彻底启用图片右键功能
    function enableImageRightClick(img) {
        if (!img || img.dataset.rightClickFixed) return;
        
        console.log('正在为图片启用右键功能:', img.src);
        
        // 标记已处理
        img.dataset.rightClickFixed = 'true';
        
        // 设置样式属性
        img.style.setProperty('pointer-events', 'auto', 'important');
        img.style.setProperty('user-select', 'auto', 'important');
        img.style.setProperty('-webkit-user-select', 'auto', 'important');
        img.style.setProperty('-moz-user-select', 'auto', 'important');
        img.style.setProperty('-ms-user-select', 'auto', 'important');
        img.style.setProperty('-webkit-touch-callout', 'default', 'important');
        img.style.setProperty('position', 'relative', 'important');
        img.style.setProperty('z-index', '9999', 'important');

        // 移除所有可能阻止右键的事件处理器
        img.oncontextmenu = null;
        img.ondragstart = null;
        img.onselectstart = null;
        img.onmousedown = null;
        img.onmouseup = null;

        // 添加强制右键事件
        img.addEventListener('contextmenu', function(e) {
            e.stopImmediatePropagation();
            console.log('图片右键菜单已强制启用');
            return true;
        }, { capture: true, passive: false });

        img.addEventListener('mousedown', function(e) {
            if (e.button === 2) { // 右键
                e.stopImmediatePropagation();
                console.log('图片右键点击已强制启用');
                return true;
            }
        }, { capture: true, passive: false });

        // 阻止父元素的事件冒泡
        img.addEventListener('click', function(e) {
            e.stopPropagation();
        }, { capture: true });
    }

    function blockZoomEvents() {
        if (zoomEventBlocked) return;
        zoomEventBlocked = true;
        
        // 禁用缩放相关函数
        if (window.ImageZoom) {
            window.ImageZoom = function() { 
                console.log('ImageZoom 被禁用'); 
                return {}; 
            };
        }
        
        if (typeof jQuery !== 'undefined' && jQuery.fn.imagezoom) {
            jQuery.fn.imagezoom = function() { 
                console.log('jQuery.fn.imagezoom 被禁用'); 
                return this; 
            };
        }

        // 拦截事件监听器
        const originalAddEventListener = Element.prototype.addEventListener;
        Element.prototype.addEventListener = function(type, listener, options) {
            // 检查是否是主图片容器
            const isMainPicContainer = hasClassContaining(this, '--mainPicWrap--');
            const isZoomMouseEvent = ['mousemove', 'mouseover', 'mouseenter', 'mouseleave', 'mouseout'].includes(type);
            
            if (isMainPicContainer && isZoomMouseEvent) {
                const listenerStr = listener.toString();
                if (listenerStr.includes('zoom') || listenerStr.includes('scale') || listenerStr.includes('lens') || listenerStr.includes('magnif')) {
                    console.log(`拦截了容器的缩放事件: ${type}`);
                    return;
                }
            }

            // 1688缩放元素
            const is1688ZoomElement = this.classList && (this.classList.contains('scale-img') || this.classList.contains('scaled-img'));
            if (is1688ZoomElement) {
                console.log(`拦截了1688缩放事件: ${type}`);
                return;
            }

            return originalAddEventListener.call(this, type, listener, options);
        };
    }

    // 添加强化的CSS样式 - 使用属性选择器
    function addZoomDisableStyles() {
        const style = document.createElement('style');
        style.id = 'zoom-disable-styles-enhanced';
        let cssText = '';

        if (is1688) {
            cssText = `
                .detail-gallery-preview .scale-img,
                .detail-gallery-preview .scaled-img {
                    display: none !important; 
                    visibility: hidden !important; 
                    opacity: 0 !important; 
                    pointer-events: none !important;
                }
                .detail-gallery-preview .preview-img {
                    pointer-events: auto !important; 
                    user-select: auto !important; 
                    -webkit-user-select: auto !important;
                    position: relative !important; 
                    z-index: 9999 !important;
                }`;
        } else if (isTaobaoTmallCommon) {
            cssText = `
                /* 彻底隐藏缩放元素 */
                .js-image-zoom__zoomed-area,
                .js-image-zoom__zoomed-image,
                #lensDiv {
                    display: none !important; 
                    visibility: hidden !important; 
                    opacity: 0 !important; 
                    pointer-events: none !important;
                    position: absolute !important;
                    top: -9999px !important;
                    left: -9999px !important;
                }

                /* 容器事件穿透 - 使用属性选择器匹配动态类名 */
                [class*="--mainPicWrap--"] {
                    pointer-events: none !important;
                }

                /* 主图片强制可交互 - 使用属性选择器 */
                [class*="--mainPicWrap--"] img[class*="--mainPic--"] {
                    pointer-events: auto !important;
                    user-select: auto !important; 
                    -webkit-user-select: auto !important; 
                    -moz-user-select: auto !important; 
                    -ms-user-select: auto !important;
                    position: relative !important;
                    z-index: 9999 !important;
                    -webkit-touch-callout: default !important; 
                    -khtml-user-select: auto !important;
                    cursor: default !important;
                }

                /* 视频元素恢复交互 */
                .videox-container,
                .videox-container video,
                .videox-container .videox-control,
                .videox-container .videox-control * {
                    pointer-events: auto !important;
                }

                /* 其他交互元素 - 使用属性选择器 */
                [class*="--thumbnail--"],
                [class*="--thumbnailPic--"] {
                    pointer-events: auto !important;
                }
                
                [class*="--switchTabsItem--"] {
                    pointer-events: auto !important;
                }
                
                [class*="--thumbnailsWrap--"],
                [class*="--thumbnails--"] {
                    pointer-events: auto !important;
                }
                
                [class*="--bottomSwitchTabsWrap--"],
                [class*="--switchTabsWrap--"] {
                    pointer-events: auto !important;
                }
            `;
        }

        style.textContent = cssText;
        const existingStyle = document.getElementById('zoom-disable-styles-enhanced');
        if (!existingStyle) {
            (document.head || document.documentElement).appendChild(style);
        } else {
            existingStyle.textContent = cssText;
        }
        console.log('CSS样式已应用 (使用动态类名匹配)');
    }

    // 全局右键修复
    function forceEnableRightClickGlobally() {
        if (rightClickFixed) return;
        rightClickFixed = true;

        // 移除全局右键阻止
        document.oncontextmenu = null;
        document.onselectstart = null;
        document.ondragstart = null;
        
        if (document.body) {
            document.body.oncontextmenu = null;
            document.body.onselectstart = null;
            document.body.ondragstart = null;
        }

        // 拦截可能阻止右键的事件
        ['contextmenu', 'selectstart', 'dragstart'].forEach(eventType => {
            document.addEventListener(eventType, function(e) {
                if (e.target.tagName === 'IMG' && e.target.src && 
                    (e.target.src.includes('alicdn.com') || e.target.src.includes('taobao.com') || e.target.src.includes('tmall.com'))) {
                    e.stopImmediatePropagation();
                    return true;
                }
            }, { capture: true, passive: false });
        });

        console.log('全局右键功能已强制启用');
    }

    // 监控动态变化
    function setupObserver() {
        const observer = new MutationObserver(function(mutations) {
            let needsReapply = false;
            
            mutations.forEach(function(mutation) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach(function(node) {
                        if (node.nodeType === 1) {
                            // 检查是否包含相关类名
                            if (hasClassContaining(node, '--mainPicWrap--') || 
                                hasClassContaining(node, '--mainPic--') || 
                                hasClassContaining(node, 'videox-container') ||
                                (node.matches && node.matches('.js-image-zoom__zoomed-area, .js-image-zoom__zoomed-image, #lensDiv, .scale-img, .scaled-img'))) {
                                needsReapply = true;
                            } else if (node.querySelector) {
                                if (node.querySelector('[class*="--mainPicWrap--"], [class*="--mainPic--"], .videox-container, .js-image-zoom__zoomed-area, .js-image-zoom__zoomed-image, #lensDiv, .scale-img, .scaled-img')) {
                                    needsReapply = true;
                                }
                            }
                        }
                    });
                }
            });

            if (needsReapply) {
                console.log('检测到DOM变化,重新应用修复');
                setTimeout(() => {
                    addZoomDisableStyles();
                    disableImageZoom();
                    forceEnableRightClickGlobally();
                }, 100);
            }
        });

        observer.observe(document.documentElement || document.body, {
            childList: true, 
            subtree: true, 
            attributes: true, 
            attributeFilter: ['class', 'style']
        });
        console.log('DOM变化监控已启动');
    }

    // 初始化函数
    function init() {
        console.log('初始化电商图片缩放禁用脚本 (动态类名修复版)...');
        
        blockZoomEvents();
        addZoomDisableStyles();
        disableImageZoom();
        forceEnableRightClickGlobally();

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => {
                console.log('DOMContentLoaded: 应用修复');
                addZoomDisableStyles();
                disableImageZoom();
                forceEnableRightClickGlobally();
                setupObserver();
            });
        } else {
            console.log('Document已加载: 应用修复并设置观察者');
            setupObserver();
        }

        // 多次延迟执行确保修复生效
        [500, 1000, 2000, 3000].forEach(delay => {
            setTimeout(() => {
                console.log(`延迟修复 (${delay}ms)`);
                addZoomDisableStyles();
                disableImageZoom();
                forceEnableRightClickGlobally();
            }, delay);
        });
    }

    init();

    // 定期维护
    const maintenanceInterval = setInterval(() => {
        addZoomDisableStyles();
        disableImageZoom();
    }, 3000);

    window.addEventListener('unload', () => {
        if (maintenanceInterval) {
            clearInterval(maintenanceInterval);
        }
    });

    console.log('电商图片缩放禁用脚本加载完成 v1.3.3 (动态类名修复版)');
})();