はじめに
電子工作の部品(ESP32やダイオードなど)をまとめ買いしようとAliExpressのカート画面を開いたところ、「勝手にページの一番下までスクロールされ続け、上に戻ろうとしても強制的に下へ引き戻される」という凶悪なバグに遭遇しました。
上のほうにある決済ボタンすら押せず、買い物が完全にブロックされる事態に。
今回は、この大手ECサイトのフロントエンドの暴走を、JavaScriptのイベント伝播の仕様を突いてたった3行のコードでねじ伏せた備忘録です。
検証環境
- OS: macOS (Tahoe 26.4.1) / MacBook M1 (2020)
- Browser: Google Chrome 148.0.7778.168 (Official Build) (arm64)
原因:無限スクロールコンポーネントの暴走
DevToolsを開いてDOMやコンソールを確認すると、原因はすぐに分かりました。
画面下部にある「おすすめ商品」を読み込むための無限スクロール(comet-v2-infinite-scroll)がバグを起こし、「まだ一番下までスクロールしていないのに、底に到達したと誤認して次々と要素をフェッチし続ける」状態になっていました。
DOMが追加されるたびにブラウザのフォーカスが下へ引っ張られるため、人間が手動でマウスホイールを上に回しても勝てない、という物理的な戦いになっていたわけです。
失敗したアプローチ: overflow: hidden の力技
最初はコンソールから手っ取り早く以下のコードを叩きました。
document.querySelectorAll('.comet-v2-infinite-scroll').forEach(el => el.style.overflow = 'hidden');
document.body.style.overflow = 'hidden';
たしかに下への暴走はピタッと止まりました。
しかし、これでは自分自身の手動スクロールすらもフリーズしてしまい、画面に映っていないアイテムの精査や決済ボタンを押すことができず、解決になりませんでした。
解決策:イベントのキャプチャフェーズで伝播を殺す
求めているのは「自分は自由にスクロールできるが、AliExpress側のJSにはスクロールしたことを検知させない」という状態です。
そこで、DOMイベントのキャプチャフェーズとstopImmediatePropagation()を使いました。
結論として、Tampermonkey等で以下のコードをページ読み込みの最速タイミングで発火させるだけです。
window.addEventListener('scroll', (e) => {
e.stopImmediatePropagation();
}, true);
なぜこれで解決するのか?
AliExpress側の無限スクロールロジックは、基本的にブラウザの scroll イベントを購読し、「現在全体の何%までスクロールされたか」を計算してトリガーを引いています。
-
第3引数の
true(キャプチャフェーズでの登録)
通常のイベントリスナーは、DOMツリーを下から上へ遡る「バブリングフェーズ」で発火します。しかし、第3引数をtrueにすると、上から下へ降りてくる「キャプチャフェーズ」でリスナーを登録できます。これにより、AliExpressがReact/Vue等で仕込んだリスナーよりも絶対に先回りしてイベントを捕まえることができます。 -
e.stopImmediatePropagation()による伝播の遮断
最速でイベントを捕まえた直後にこれを呼ぶことで、同一要素に設定された他のリスナーへの伝播を完全にシャットアウトします。
結果として、ブラウザのネイティブなスクロール挙動(これはJSのイベントではなくブラウザのデフォルト機能)は正常に機能しつつ、AliExpress側のスクリプトには「ユーザーは1ミリもスクロールしていない」と誤認させることに成功しました。無限スクロールのトリガーが引かれないため、暴走は起きません。
Tampermonkey用の完全版スクリプト
毎回コンソールを開くのも手間なので、Tampermonkeyに登録して自動化しました。
(※カート画面の追従UIなどが動かなくなるのを防ぐため、念のためある程度下へスクロールした時だけイベントを殺すように条件を足しています)
// ==UserScript==
// @name AliExpress カート無限スクロール暴走停止
// @namespace http://tampermonkey.net/
// @version 1.0
// @description scrollイベントの伝播をキャプチャフェーズで阻止し、無限スクロールのトリガーを引かせないようにします
// @match https://www.aliexpress.com/p/shoppingcart/*
// @match https://*.aliexpress.com/p/shoppingcart/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
// スクロールイベントを最速でキャッチして、サイト側のプログラムへの伝播を遮断
window.addEventListener('scroll', (e) => {
// おすすめエリア(無限スクロールのトリガー付近)のみイベントを殺す
if (window.scrollY > 300) {
e.stopImmediatePropagation();
}
}, true);
})();
おわりに
普段何気なく使っているブラウザの標準API(イベントのバブリングとキャプチャリング)ですが、その仕様をちゃんと理解していると、今回のように他人が書いたブラックボックスなJSの暴走すらも、たった数行のコードで外側から安全にコントロールできます。
AliExpressのカートで同じように上に戻れずキレそうになっている方がいれば、ぜひ試してみてください。