简体   繁体   English

使用 Tampermonkey 在 AngularJS 网站上自动提交表单?

[英]Automate form submission, on an AngularJS website, using Tampermonkey?

I'm trying to automate some form entry on a website that I do not have the source code to.我正在尝试在我没有源代码的网站上自动执行某些表单输入。 Finding the appropriate fields and filling them out with js and submitting the form programmatically is a simple enough task.找到合适的字段并用 js 填写它们并以编程方式提交表单是一项足够简单的任务。 But the website is built in Angular and, when the form submit is clicked, all the validation flags for the input fields pop up as if none of the fields were filled out.但是该网站是用 Angular 构建的,当单击表单提交时,输入字段的所有验证标志都会弹出,就好像没有填写任何字段一样。

Browsing several other posts, I came across the realization that I need to somehow either set the variable inside a scope, like this:浏览其他几篇文章,我意识到我需要以某种方式在范围内设置变量,如下所示:

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

or to set the field to dirty, like this:或者将字段设置为脏,如下所示:

$scope.fieldname.$dirty = true;

Unfortunately, not having access to the code, I'm unsure of what the scope might be, or how to appropriately tell Angular that the fields on the form have been updated programmatically.不幸的是,无法访问代码,我不确定范围可能是什么,或者如何适当地告诉 Angular 表单上的字段已以编程方式更新。

Edit编辑

I'm using Roblox as an example for this error.我使用 Roblox 作为此错误的示例。
Different forms on the site such as the registration form (and forms behind the login) have validation on them, which throws the errors I mentioned.网站上的不同表单,例如注册表单(以及登录后的表单)都经过验证,这会引发我提到的错误。

Here's an example I made of trying to use the same logic as the login script on the register script:这是我尝试使用与注册脚本上的登录脚本相同的逻辑的示例:

// ==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( );

I have tried to add both the input and change events to my calls after changing the values, but there was no change in updating the validation to the values I added to the input fields.在更改值后,我尝试将输入和更改事件都添加到我的调用中,但是在将验证更新为我添加到输入字段的值时没有任何变化。 For example:例如:

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

You can test both of those script by adding them to Tampermonkey, and navigating to the ssl roblox homepage .您可以通过将它们添加到 Tampermonkey 并导航到ssl roblox 主页来测试这两个脚本。

The key point is that that Angular code (and similar JS) use change events to trigger their validators.关键是 Angular 代码(和类似的 JS)使用change事件来触发它们的验证器。 So, just setting the value is not enough;所以,仅仅设置值是不够的; you must also send a change event.您还必须发送更改事件。

So:所以:

  1. Set the value.设置值。
  2. Send a change event.发送更改事件。 For Greasemonkey/Tampermonkey scripts you must be mindful of the sandboxes and jQuery conflicts for this part.对于 Greasemonkey/Tampermonkey 脚本,您必须注意这部分的沙箱和 jQuery 冲突。
    Using a jQuery .change() , or .trigger() , from a userscript that is not injected, seldom works.使用来自未注入的用户脚本的jQuery .change().trigger()很少起作用。 Send a proper change event;发送适当的更改事件; see below.见下文。
  3. Since you @require d jQuery (good), but used @grant none (bad), your script was causing the page to crash and you would see various errors in the console.由于您@require d jQuery(好),但使用了@grant none (坏),您的脚本导致页面崩溃,您会在控制台中看到各种错误。
  4. The script has a race condition and was often firing before the inputs were ready.该脚本具有竞争条件,并且经常在输入准备就绪之前触发。 Waiting for window.load seems to be enough, but you may have to step up to something like waitForKeyElements() if you should see more timing problems.等待window.load似乎就足够了,但是如果您应该看到更多计时问题,您可能需要加强类似waitForKeyElements()方法。
  5. There may be additional timing issues with the $('#SignupButton').click (); $('#SignupButton').click ();可能存在其他计时问题$('#SignupButton').click (); If so, that's outside the scope of this question.如果是这样,那超出了这个问题的范围。
  6. DO NOT USE THIS KNOWLEDGE TO VIOLATE ANY WEBSITE'S TERMS OF SERVICE.请勿利用此知识违反任何网站的服务条款。 (If applicable) (如果适用)

With that, the script becomes:这样,脚本就变成了:

// ==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);
}


Note that we use a valid @grant value, other than none .请注意,我们使用有效的@grant值,而不是none That's crucial to avoid conflicts with the page's javascript.这对于避免与页面的 javascript 发生冲突至关重要。

it turns out there are several different events that trigger validation on various websites (input, keyup, change).事实证明,有几个不同的事件会触发各种网站上的验证(输入、键盘输入、更改)。 here is the function i use that seems to work on all websites (jquery, angularjs, or plain old javascript):这是我使用的似乎适用于所有网站(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);
}

in this specific case, i think this jquery should work:在这种特定情况下,我认为这个 jquery 应该可以工作:

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

you might want to be careful to only fire the "change" event if the value was actually changed.如果值实际更改,您可能需要小心仅触发“更改”事件。 i have seen some sloppy code on checkboxes toggling "valid" on every "change" event rather than reading the actual checkbox value.我看到复选框上的一些草率代码在每个“更改”事件上切换“有效”,而不是读取实际的复选框值。

note: tested on chrome 46.0.2490.71 (portable_apps edition)注意:在 chrome 46.0.2490.71(portable_apps 版)上测试

I was not able to get it work using the code here to set a contact field in the nextcloud contacts app (that is written in angular).我无法使用此处的代码在 nextcloud 联系人应用程序中设置联系人字段(以 angular 编写)使其工作。 This is what worked for me:这对我有用:

function setAngularInputValue (targetInput, newValue) {
    targetInput.value = newValue;
    const event       = new Event('input', { bubbles: true });

    targetInput.dispatchEvent(event);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM