如果有人問你,要怎麼寫 JavaScript 在以下網頁的 textarea 欄位塞值並按下送出鈕,你可能會像我噗哧一笑,想說這是什麼白痴問題?

然後用肌肉記憶生出兩行程式碼:

document.querySelector('textarea.ant-input').value = 'summary';
document.querySelector('button.ant-btn').click();

漫不在乎按下 Enter,正要帥氣轉身,高歌離席... 不料,網頁沒送出,登楞!

試了五分鐘換了幾種寫法,textarea 有出現文字,但滑鼠點了欄位文字會消失;程式無法觸發按鈕,趁 textarea 有內容,手動按鈕也無法送出。我這才意識到,代誌不像憨人想得那麼簡單...

經過一番調查,大概有了頭緒。網頁是用 React.js 寫的,靠 Virtual DOM 驅動,直接修改表象的 HTML 元素是沒意義的,背後對映的值沒改改,下一輪 Render 時,HTML 元素的值便會被覆寫。這就是為什麼去點 textarea,JavaScript 寫入的值會消失;手動按鈕無效則是因為從 React.js 角度 textarea 是空的不能送出。

江湖一點訣,講破不值錢。查到要在 React.js 網頁模擬輸入及 Click 的寫法。參考 1 參考 2

const textAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
function simulateTextareaInput(input, newValue) {
    textAreaValueSetter.call(input, newValue);
      const event = new Event('input', { bubbles: true });
      event.simulated = true;
      input.dispatchEvent(event);
}
const mouseClickEvents = ['mousedown', 'click', 'mouseup'];
function simulateMouseClick(element){
  mouseClickEvents.forEach(mouseEventType =>
    element.dispatchEvent(
      new MouseEvent(mouseEventType, {
          view: window,
          bubbles: true,
          cancelable: true,
          buttons: 1
      })
    )
  );
}
simulateTextareaInput(document.querySelector('textarea.ant-input'), 'summary');
simulateMouseClick(document.querySelector('button.ant-btn'));

背後原理是因為 React 覆寫了 value 屬性 setter,故我們要改用 prototype.value 原生的 setter 更新 value,再觸發 input 事件。模擬點擊則要連續觸發 mousedown、click、mouseup 事件。

The web page developed with React.js cannot be modified using the regular DOM API due to the Virtual DOM. This article introduces how to simulate input and button clicks.


Comments

# by Anonymous

Tampermonkey?

Post a comment