Image download4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// ==UserScript==
// @name Image Download4
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Add a download icon to images larger than 400px when hovered.
// @author Your Name
// @match *://*/*
// @run-at document-idle
// @grant GM_download
// ==/UserScript==
// bug: icon position
(function() {
'use strict';
// Create the download icon
const icon = document.createElement('div');
icon.textContent = '\u2B73'; // A simple arrow icon
icon.style.position = 'absolute';
icon.style.width = '24px';
icon.style.height = '24px';
icon.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
icon.style.color = 'white';
icon.style.fontSize = '18px';
icon.style.textAlign = 'center';
icon.style.lineHeight = '24px';
icon.style.borderRadius = '50%';
icon.style.cursor = 'pointer';
icon.style.display = 'none';
icon.style.zIndex = '10000';
document.body.appendChild(icon);
let currentImg = null;
let iconVisible = false;
// Function to check image size
function isLargeImage(img) {
return img.naturalWidth > 400 && img.naturalHeight > 400;
}
function getPosition (element) {
var x = 0;
var y = 0;
// 搭配上面的示意圖可比較輕鬆理解為何要這麼計算
while ( element ) {
x += element.offsetLeft - element.scrollLeft + element.clientLeft;
y += element.offsetTop - element.scrollLeft + element.clientTop;
element = element.offsetParent;
}
return { x: x, y: y };
}
// get document coordinates of the element
function getCoords(elem) {
let box = elem.getBoundingClientRect();
return {
top: box.top + window.pageYOffset,
right: box.right + window.pageXOffset,
bottom: box.bottom + window.pageYOffset,
left: box.left + window.pageXOffset
};
}
// Mouse move event on images
document.addEventListener('mouseover', (e) => {
const img = e.target;
if (img.tagName === 'IMG' && isLargeImage(img) && !iconVisible) {
console.log('move');
/*let position = getPosition(img);
icon.style.left = `${position.x+100}px`;
icon.style.top = `${position.y}px`;*/
let position = getCoords(img);
icon.style.left = `${position.left+24}px`;
icon.style.top = `${position.bottom-24}px`;
//const rect = img.getBoundingClientRect();
//icon.style.left = `${rect.right - 30}px`;
//icon.style.top = `${rect.bottom - 30}px`;
icon.style.display = 'block';
currentImg = img;
iconVisible = true;
}
});
//detect if URL has changed
// work on twitter
window.navigation.addEventListener("navigate", (e) => {
//same as above
const img = e.target;
if (img.tagName === 'IMG' && isLargeImage(img) && !iconVisible) {
console.log('move');
/*let position = getPosition(img);
icon.style.left = `${position.x+100}px`;
icon.style.top = `${position.y}px`;*/
let position = getCoords(img);
icon.style.left = `${position.left+24}px`;
icon.style.top = `${position.bottom-24}px`;
//const rect = img.getBoundingClientRect();
//icon.style.left = `${rect.right - 30}px`;
//icon.style.top = `${rect.bottom - 30}px`;
icon.style.display = 'block';
currentImg = img;
iconVisible = true;
}
})
document.addEventListener('mouseout', (e) => {
if (e.target === currentImg && e.relatedTarget !== icon) {
console.log('out');
icon.style.display = 'none';
currentImg = null;
iconVisible = false;
}
});
// Download image on icon click
icon.addEventListener('click', () => {
if (currentImg) {
const imageUrl = currentImg.src;
const fileName = imageUrl.split('/').pop().split('?')[0];
GM_download(imageUrl, fileName + '.jpg');
}
});
})();
我可以幫您融合這兩個事件監聽器,因為它們的內部邏輯是相同的。以下是融合後的程式碼,使用一個共用的函數來處理圖片滑鼠事件的邏輯:
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定義共用的處理函數
function handleImageHover(img) {
if (img.tagName === 'IMG' && isLargeImage(img) && !iconVisible) {
console.log('move');
let position = getCoords(img);
icon.style.left = `${position.left + 24}px`;
icon.style.top = `${position.bottom - 24}px`;
icon.style.display = 'block';
currentImg = img;
iconVisible = true;
}
}
// Mouse move event on images
document.addEventListener('mouseover', (e) => {
handleImageHover(e.target);
});
// Detect if URL has changed (works on Twitter)
window.navigation.addEventListener("navigate", (e) => {
handleImageHover(e.target);
});
融合後的改進點:
-
提取了重複的邏輯到 handleImageHover 函數,避免程式碼重複
-
修正了模板字符串語法(使用正確的 ${} 包裹)
-
保持了原有的功能不變,只是組織方式更優雅
-
更容易維護和修改(只需改一個地方的邏輯)
注意事項:
-
假設 isLargeImage、icon、currentImg 和 getCoords 等變數/函數已在其他地方定義
-
如果您需要不同的偏移量(比如 mouseover 用 +24,而 navigate 用其他值),可以在函數中添加參數來處理
twitter 剛開始用是好的。但滾輪在往下捲到底,他自動載入之後,我的這個程式會失效
15 web pages
你的問題描述了在使用 Twitter 時,你的 JavaScript 程式在一開始運作正常,但當頁面滾動到底部並觸發 Twitter 的無限滾動(infinite scroll)自動載入新內容後,程式就失效了。這是一個常見的問題,因為 Twitter 的無限滾動會動態新增 DOM 元素,而你的程式可能只對初始載入的元素綁定了事件監聽器,沒有處理後續動態載入的內容。
根據你之前提供的程式碼,我假設你的程式是在監聽圖片的 mouseover 事件,並在特定條件下顯示一個圖標(icon)。當 Twitter 載入新內容時,新加入的圖片沒有被你的程式監聽,因此事件不會觸發。以下是可能的解決方案:
解決方法
1. 使用 MutationObserver 監聽 DOM 變化
Twitter 的無限滾動會動態添加新的 DOM 元素(例如新的圖片)。你可以使用 MutationObserver 來監測 DOM 的變化,並在新圖片加入時重新綁定事件。
以下是改進後的程式碼範例:
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 定義共用的處理函數
function handleImageHover(img) {
if (img.tagName === 'IMG' && isLargeImage(img) && !iconVisible) {
console.log('move');
let position = getCoords(img);
icon.style.left = `${position.left + 24}px`;
icon.style.top = `${position.bottom - 24}px`;
icon.style.display = 'block';
currentImg = img;
iconVisible = true;
}
}
// 綁定事件到現有的圖片
function bindEventsToImages() {
document.querySelectorAll('img').forEach(img => {
img.addEventListener('mouseover', () => handleImageHover(img));
});
}
// 初始綁定
bindEventsToImages();
// 使用 MutationObserver 監聽 DOM 變化
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length) {
// 當有新節點加入時,重新綁定事件
bindEventsToImages();
}
});
});
// 監聽 document.body 的子節點變化
observer.observe(document.body, {
childList: true,
subtree: true
});
// 對於 navigation 事件(URL 變化)
window.navigation.addEventListener("navigate", (e) => {
handleImageHover(e.target);
});
說明:
-
MutationObserver 會監聽整個頁面的 DOM 變化。
-
當 Twitter 載入新內容(例如新的圖片)時,bindEventsToImages 會重新執行,為新加入的圖片綁定 mouseover 事件。
-
這樣即使頁面滾動到底部並載入新內容,你的程式也能繼續運作。
2. 事件委派(Event Delegation)
另一個更簡單的解決方法是使用事件委派。不是直接將事件綁定到每個圖片,而是將事件監聽器綁定到一個不會變動的父元素(例如 document),然後檢查事件目標是否為圖片。
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定義共用的處理函數
function handleImageHover(img) {
if (img.tagName === 'IMG' && isLargeImage(img) && !iconVisible) {
console.log('move');
let position = getCoords(img);
icon.style.left = `${position.left + 24}px`;
icon.style.top = `${position.bottom - 24}px`;
icon.style.display = 'block';
currentImg = img;
iconVisible = true;
}
}
// 使用事件委派監聽所有 mouseover 事件
document.addEventListener('mouseover', (e) => {
handleImageHover(e.target);
});
// 對於 navigation 事件(URL 變化)
window.navigation.addEventListener("navigate", (e) => {
handleImageHover(e.target);
});
說明:
-
事件委派利用事件冒泡機制,將監聽器放在 document 上。
-
無論 Twitter 何時載入新圖片,只要觸發 mouseover 事件,程式都能捕捉到並處理。
-
這比 MutationObserver 更輕量,因為不需要持續監測 DOM 變化。
3. 檢查失效的原因
如果上述方法仍無法解決問題,可能是因為:
-
全局變數問題:檢查 icon、currentImg 和 iconVisible 是否在 Twitter 載入新內容後被重置或變得不可用。
-
Twitter 的 JavaScript 衝突:Twitter 可能在載入新內容時執行了自己的腳本,覆蓋了你的樣式或事件。你可以用瀏覽器的開發者工具(F12)檢查 icon 的樣式是否被改變。
你可以添加以下除錯程式碼來確認:
javascript
1
2
3
4
5
6
document.addEventListener('mouseover', (e) => {
console.log('Target:', e.target);
console.log('Icon Visible:', iconVisible);
console.log('Icon Element:', icon);
handleImageHover(e.target);
});
建議
-
首選方案:使用「事件委派」(方法 2),因為它簡單高效,適用於動態內容。
-
備用方案:如果需要更精確控制新載入的元素,可以使用 MutationObserver(方法 1)。
-
測試:在 Twitter 上測試時,滾動到底部並等待新內容載入,確認圖標是否仍能正確顯示。
如果還有其他問題或需要進一步協助,請提供更多細節(例如完整的程式碼或失效時的具體行為),我會幫你調整!
你的程式碼整體運作良好,但你提到了一個問題:有時圖標(icon)會卡在某個圖片上不消失。這個問題可能與 mouseout 事件的觸發條件或邏輯有關。我檢查了你的程式碼,發現以下幾個可能的原因及解決方案:
問題分析
-
mouseout 事件的條件問題:
-
你在 mouseout 事件中檢查了 e.target === currentImg && e.relatedTarget !== icon,這意味著只有當滑鼠從 currentImg 移出且移到的目標不是 icon 時,才會隱藏圖標。
-
如果滑鼠移動到一個不符合條件的元素(例如某些覆蓋層、子元素或非預期的目標),icon 不會隱藏,就會出現「卡住」的情況。
-
-
動態內容影響:
- 在 Twitter 或其他動態網站上,圖片可能會因為頁面滾動或重新渲染而改變位置或結構,這可能導致 currentImg 的參考失效,或者 mouseout 事件未正確觸發。
-
iconVisible 狀態未正確重置:
- 如果某些情況下 iconVisible 沒有被設為 false,下次滑鼠移入新圖片時,handleImageHover 的條件 !iconVisible 會阻止圖標更新位置。
解決方案
以下是修復「圖標卡住」問題的改進版本程式碼,並針對 Twitter 的無限滾動問題使用事件委派:
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// ==UserScript==
// @name Image Download4
// @namespace http://tampermonkey.net/
// @version 1.3
// @description Add a download icon to images larger than 400px when hovered.
// @author Your Name
// @match *://*/*
// @run-at document-idle
// @grant GM_download
// ==/UserScript==
(function() {
'use strict';
// Create the download icon
const icon = document.createElement('div');
icon.textContent = '\u2B73'; // A simple arrow icon
icon.style.position = 'absolute';
icon.style.width = '24px';
icon.style.height = '24px';
icon.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
icon.style.color = 'white';
icon.style.fontSize = '18px';
icon.style.textAlign = 'center';
icon.style.lineHeight = '24px';
icon.style.borderRadius = '50%';
icon.style.cursor = 'pointer';
icon.style.display = 'none';
icon.style.zIndex = '10000';
document.body.appendChild(icon);
let currentImg = null;
let iconVisible = false;
// Function to check image size
function isLargeImage(img) {
return img.naturalWidth > 400 && img.naturalHeight > 400;
}
// Get document coordinates of the element
function getCoords(elem) {
let box = elem.getBoundingClientRect();
return {
top: box.top + window.pageYOffset,
right: box.right + window.pageXOffset,
bottom: box.bottom + window.pageYOffset,
left: box.left + window.pageXOffset
};
}
function handleImageHover(img) {
if (img.tagName === 'IMG' && isLargeImage(img)) {
// 如果已有圖標顯示,先隱藏並重置
if (iconVisible && currentImg !== img) {
icon.style.display = 'none';
iconVisible = false;
}
if (!iconVisible) {
let position = getCoords(img);
icon.style.left = `${position.left + 24}px`;
icon.style.top = `${position.bottom - 24}px`;
icon.style.display = 'block';
currentImg = img;
iconVisible = true;
}
}
}
// Hide icon when leaving image or icon
function hideIconIfNeeded(e) {
const fromElement = e.target;
const toElement = e.relatedTarget;
// 如果離開的是當前圖片或圖標,且目標不再是圖片或圖標
if ((fromElement === currentImg || fromElement === icon) &&
toElement !== icon && toElement !== currentImg) {
icon.style.display = 'none';
currentImg = null;
iconVisible = false;
}
}
// Mouseover event delegation
document.addEventListener('mouseover', (e) => {
const img = e.target;
handleImageHover(img);
});
// Mouseout event delegation
document.addEventListener('mouseout', (e) => {
hideIconIfNeeded(e);
});
// Handle navigation (URL change) for Twitter compatibility
window.navigation?.addEventListener("navigate", (e) => {
const img = e.target;
handleImageHover(img);
});
// Download image on icon click
icon.addEventListener('click', () => {
if (currentImg) {
const imageUrl = currentImg.src;
const fileName = imageUrl.split('/').pop().split('?')[0];
GM_download(imageUrl, fileName + '.jpg');
}
});
})();
改進點
-
修復圖標卡住問題:
-
在 handleImageHover 中,新增了檢查:如果已有圖標顯示且當前圖片不是新目標圖片,先隱藏圖標並重置狀態。
-
在 mouseout 事件中,改進了隱藏邏輯,檢查是否從圖片或圖標移出,並確保目標不再是圖片或圖標時才隱藏。
-
-
事件委派:
- 使用 document.addEventListener(‘mouseover’, …) 和 document.addEventListener(‘mouseout’, …) 處理所有圖片的事件,即使 Twitter 動態載入新內容也能正常運作。
-
移除無用函數:
- 移除了未使用的 getPosition 函數,保持程式碼簡潔。
-
Twitter 兼容性:
- 保留了 navigate 事件的處理,並添加了可選鏈運算符 ?. 以防止某些瀏覽器不支持 window.navigation 時報錯。
Comments powered by Disqus.