6

I'm trying to finish a script that fills out a checkout form. It needs to click a button with no id or name then fill out the rest of the form that appears. The code I have to click the button is:

document.querySelector('input[type="submit"][value="continue"]').click();

but that is not working for me, the other elements will appear after that, my script:

// ==UserScript==
// @name         Script
// @include      https://checkout.bigcartel.com/*
// @include      http://*.bigcartel.com/product
// @include      http://*.bigcartel.com/cart
// @grant        none
// ==/UserScript==

// on "/cart" page click checkout button
if (window.location.origin !== "https://checkout.bigcartel.com") document.getElementsByName("checkout")[0].click();
else {
    // fill first three form fields
    document.getElementById("buyer_first_name").value = "John";
    document.getElementById("buyer_last_name").value = "Smith";
    document.getElementById("buyer_email").value = "john@doe.com";
    //click button for next section 
    document.querySelector('input[type="submit"][value="continue"]').click();
}
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
oversoon
  • 350
  • 2
  • 7
  • 21
  • you need to load the script "later" ... perhaps look up documentation for [@run-at](https://tampermonkey.net/documentation.php#_run_at) – Jaromanda X Dec 05 '17 at 01:14

1 Answers1

5

Refer to Choosing and activating the right controls on an AJAX-driven site, and use a utility like waitForKeyElements().

Here's how to wait for elements and chain states (in case it's needed) for the submit click:

// ==UserScript==
// @name     _Wait for elements overkill and on separate pages
// @match    https://checkout.bigcartel.com/*
// @match    *://*.bigcartel.com/product*
// @match    *://*.bigcartel.com/cart*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant    GM_addStyle
// ==/UserScript==
//- The @grant directive is needed to restore the proper sandbox.

//-- Track form field state, just in case. *Might* be needed for multistage forms...
var glblFlags = {firstFlld: false, lastFlld: false, emailFlld: false};

if (location.hostname === "checkout.bigcartel.com") {
    /*-- Fill first three form fields.
        Use separate WFKE's for multistage.  (Fun overkill for simpler forms.)
    */
    waitForKeyElements ("#buyer_first_name", jNd => {SetValue (jNd, "firstFlld", "John"); }, true);
    waitForKeyElements ("#buyer_last_name",  jNd => {SetValue (jNd, "lastFlld",  "Smith"); }, true);
    waitForKeyElements ("#buyer_email",      jNd => {SetValue (jNd, "emailFlld", "john@doe.com"); }, true);

    //-- Click button for next section
    waitForKeyElements (
        "form[action='/shipping'] button:contains('Next')",
        clickWhenFormIsReady, true
    );
}
else if (location.pathname === "/cart") {
    //-- On "/cart" page click checkout button
    waitForKeyElements ("[name='checkout']", clickNode, true);
}

function clickNode (jNode) {
    var clickEvent  = document.createEvent ('MouseEvents');
    clickEvent.initEvent ('click', true, true);
    jNode[0].dispatchEvent (clickEvent);
}

function SetValue (jNode, flagVarName, newValue) {
    jNode.val (newValue);
    glblFlags[flagVarName] = true;
}

function clickWhenFormIsReady (jNode) {
    //-- Keep waiting if all flags are not true (form not yet filled out):
    for (let prpName in glblFlags) {
        if (glblFlags.hasOwnProperty (prpName) ) {
            if (glblFlags[prpName] === false)
                return true;
        }
    }
    clickNode (jNode);
}

To add additional form fields, you would:

  1. Add a new "filled" property to the glblFlagsobject. For example:

    var glblFlags = {firstFlld: false, lastFlld: false, emailFlld: false, phoneFlld: false};
    
  2. Add a new waitForKeyElements line to the first if section. For example:

        waitForKeyElements ("#buyer_phone", jNd => {SetValue (jNd, "phoneFlld", "800-867-5309"); }, true);
    

    The second parameter to SetValue() is the name of the property you added in step 1.
    The third parameter to SetValue() is the value you wish to fill in the form field with.

  3. Some form controls may require special handling. That's beyond the scope of this question. Search around and then open a new question, if needed.

Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • Thanks for your input. Your code partially worked for me. I had to click the checkout button but your code filled out the three fields and clicked the button. Am I missing something? – oversoon Dec 05 '17 at 19:45
  • Could you explain how your "waitForKeyElements ("#buyer_first_name", jNd => {SetValue (jNd, "firstFlld", "John"); }, true);" line works so I can learn when writing the code to finish filling in the form? – oversoon Dec 05 '17 at 20:55
  • Yes, something you have not shown is the problem. Check: (1) for iframes, (2) `location.pathname`, (3) the match statements, (4) `[name='checkout']`. ... Also, **link to the target page or provide a suitable MCVE.** – Brock Adams Dec 05 '17 at 20:56
  • Thanks dude. I'm trying to make an auto check out script for bigcartel stores, like http://groundup.bigcartel.com. So when something gets added to the cart it completes check out – oversoon Dec 05 '17 at 21:19
  • Is it possible to use WFKE to select from a drop down with something like, "waitForKeyElements ("select[name='shipping_country_id']option[value='42']", clickNode, true);" – oversoon Dec 05 '17 at 22:32
  • Okay, based on the actual page, updated the answer script. ... And yes that WFKE is possible, except that you need a space in front of `option`. Anyway, this question has morphed enough. – Brock Adams Dec 05 '17 at 22:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160641/discussion-between-oversoon-and-brock-adams). – oversoon Dec 06 '17 at 17:57