2015-12-18 220 views
3

我試圖自動化一個網站上的一些表單條目,我沒有源代碼。找到合適的字段並用js填充並以編程方式提交表單是一項非常簡單的任務。但是該網站是建立在Angular中的,當點擊表單提交時,所有輸入字段的驗證標誌彈出,就好像沒有任何字段被填寫一樣。使用Tampermonkey在AngularJS網站上自動完成表單提交?

瀏覽其他幾個職位,我碰到認識到我需要以某種方式要麼設置變量的作用域裏,就像這樣:

$scope.$apply(function() { 
    $scope.fieldName = varname; 
}); 

或將字段設置爲髒,像這樣:

$scope.fieldname.$dirty = true; 

不幸的是,沒有訪問代碼,我不確定範圍可能是什麼,或者如何適當地告訴Angular表單上的字段已經以編程方式更新。

編輯

我使用Roblox爲這個錯誤的例子。
網站上的不同形式(如註冊表單(以及登錄背後的表單))對其進行驗證,這會引發我提到的錯誤。

這是我做的嘗試使用相同的邏輯,對寄存器腳本登錄腳本的例子:

// ==UserScript== 
// @name   Roblox Account Create 
// @namespace http://roblox.com/ 
// @version  0.1 
// @description Create Roblox Account 
// @author  You 
// @match  https://www.roblox.com/ 
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js 
// @grant  none 
// ==/UserScript== 
/* jshint -W097 */ 
'use strict'; 

var _adj  = [ 'Cool', 'Masked', 'Bloody', 'Lame' ]; 
var _animals = [ 'Hamster', 'Moose', 'Lama', 'Duck' ]; 
var _months  = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; 

var _username = _adj[ parseInt(Math.random() * _adj.length) ], _pass = Math.random().toString(36).substring(2, 10); 
_username  += _animals[ parseInt(Math.random() * _animals.length) ]; 
_username  += parseInt(Math.random() * 1000); 

var _dt_month = _months[ Math.floor(Math.random() * (12) + 0) ]; 
var _dt_day  = Math.floor(Math.random() * (28) + 1); 
var _dt_year = Math.floor(Math.random() * (2005 - 1916 + 1) + 1916); 

$('#Username').val(_username); 

$('#Password').val(_pass); 
$('#PasswordConfirm').val(_pass); 
$('#MonthDropdown').val(_dt_month); 
$('#DayDropdown').val(_dt_day); 
$('#YearDropdown').val(_dt_year); 

$('#FemaleButton').click(); 
$('#SignupButton').click(); 

我已嘗試添加輸入和更改值後更改事件我電話,但在將驗證更新爲添加到輸入字段的值方面沒有任何變化。例如:

$('#Username').val(_username).trigger('input'); // Also .trigger('change') 

您可以將它們添加到Tampermonkey,並導航到ssl roblox homepage測試這兩個腳本。

+0

這將是很高興有網頁的URL測試。 – wOxxOm

+0

我正在製作此腳本的網站是受IP限制的白名單。我確實有幾個網站有篡改猴自動登錄頁面加載爲我。其中一個網站也使用angularjs,因此我遇到了同樣的問題:[Roblox](http://www.roblox.com/) – Mike

+0

@BrockAdams,我已更新我的帖子,以包括相關的測試網址以及測試腳本來複制問題。 – Mike

回答

4

關鍵是,Angular代碼(和類似的JS)使用change事件來觸發它們的驗證器。所以,只設定價值是不夠的;您還必須發送更改事件。

所以:

  1. 設置的值。
  2. 發送更改事件。對於Greasemonkey/Tampermonkey腳本,您必須注意這部分的沙箱和jQuery衝突。
    使用jQuery.change().trigger(),來自未注入的userscript,很少有效。發送適當的更改事件;見下文。
  3. 由於你@require d jQuery(好),但使用@grant none(壞),你的腳本導致頁面崩潰,你會看到控制檯中的各種錯誤。
  4. 該腳本具有競爭條件,並且在輸入準備好之前經常發射。等待window.load似乎已經足夠了,但是如果您應該看到更多的時間問題,您可能必須採取類似waitForKeyElements()的措施。
  5. $('#SignupButton').click();可能還有其他計時問題如果是這樣,那就超出了這個問題的範圍。
  6. 請勿使用此知識來破壞任何網站的服務條款。 (如果適用)

有了,腳本變爲:

// ==UserScript== 
// @name   Roblox Account Create 
// @match  https://www.roblox.com/ 
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js 
// @grant  GM_addStyle 
// ==/UserScript== 
var _adj  = [ 'Cool', 'Masked', 'Bloody', 'Lame' ]; 
var _animals = [ 'Hamster', 'Moose', 'Lama', 'Duck' ]; 
var _months  = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; 
var _username = _adj[ parseInt(Math.random() * _adj.length) ], _pass = Math.random().toString(36).substring(2, 10); 
_username  += _animals[ parseInt(Math.random() * _animals.length) ]; 
_username  += parseInt(Math.random() * 1000); 
var _dt_month = _months[ Math.floor(Math.random() * (12) + 0) ]; 
var _dt_day  = Math.floor(Math.random() * (28) + 1); 
var _dt_year = Math.floor(Math.random() * (2005 - 1916 + 1) + 1916); 

window.addEventListener ("load", function load() { 
    setControl ('Username', _username); 
    setControl ('Password', _pass); 
    setControl ('PasswordConfirm', _pass); 
    setControl ('MonthDropdown', _dt_month); 
    setControl ('DayDropdown', _dt_day); 
    setControl ('YearDropdown', _dt_year); 
    $('#FemaleButton').click(); 
    $('#SignupButton').click(); 
}); 

function setControl (elemID, value) { 
    var zInput = $('#' + elemID); 
    zInput.val(value); 

    var changeEvent = document.createEvent ("HTMLEvents"); 
    changeEvent.initEvent ("change", true, true); 
    zInput[0].dispatchEvent (changeEvent); 
} 


注意,我們使用一個有效的@grant值,比none等。這對於避免與頁面的JavaScript發生衝突至關重要。

+1

我現在明白我的問題所在。我對Angular並不是太熟悉,但是這很有效,你的迴應可以幫助我理解一大堆! – Mike

+0

不客氣,樂意效勞! –

+0

備註:我不必使用授權或導入jquery來調用createEvent,initEvent和dispatchEventl,它解決了我的問題。儘管我確實認識到使用授權來隔離你的名字空間的價值。 –

1

事實證明,有幾個不同的事件觸發各種網站驗證(輸入,鍵入,更改)。這裏是我使用似乎在所有網站上運行(jQuery的,angularjs,或傳統的JavaScript)的功能:在這種特殊情況下

function fireChangeEvents(element){ 
    var changeEvent = null; 
    changeEvent = document.createEvent ("HTMLEvents"); 
    changeEvent.initEvent ("input", true, true); 
    element.dispatchEvent (changeEvent); 
    console.debug('input event dispatched for element: '+element.id); 
    changeEvent = document.createEvent ("HTMLEvents"); 
    changeEvent.initEvent ("keyup", true, true); 
    element.dispatchEvent (changeEvent); 
    console.debug('keyup event dispatched for element: '+element.id); 
    changeEvent = document.createEvent ("HTMLEvents"); 
    changeEvent.initEvent ("change", true, true); 
    element.dispatchEvent (changeEvent); 
    console.debug('change event dispatched for element: '+element.id); 
} 

,我覺得這個jQuery應該工作:

fireChangeEvents(document.getElementById('Username')); 

如果實際更改了值,則可能需要小心才觸發「更改」事件。我已經看到複選框上的一些草率代碼在每個「更改」事件上切換「有效」,而不是讀取實際的複選框值。

備註:在Chrome上測試46.0.2490.71(portable_apps版本)