Search code examples
javascriptjqueryhtmluserscriptstampermonkey

Setting the value of a Dropdown associated with an id node in the same HTML section


I'm looking to select a specific dropdown value from dozens of HTML dropdowns for a userscript. Is there a way to do this using the ID I have ({"Id":"302"})?

I've tried selecting all items based on the class as a jumping off point but I'm not having much success. Ideally though if I could select based on the provided ID it would allow me to be more specific with the selections.

What I have:

waitForDropdown (".control:has(option[value='See Notes'])", selectDropdown);

function selectDropdown (jNode) {
    var evt = new Event ("click");
    jNode[0].dispatchEvent (evt);

    jNode.val('See Notes');

    evt = new Event ("change");
    jNode[0].dispatchEvent (evt);
}

This is the HTML:

<div class="field tabular">
    <span class="item-data">{"Id":"302"}</span>
    <div class="field-content">
        <div class="title" title="Dropdown A">Dropdown A</div>
        <div class="data">
            <div class="errors"></div>
            <div class="control">
                <select>
                    <option value="Not Checked" selected="selected">Not Checked</option>
                    <option value="Checked &amp; Cleaned">Checked &amp; Cleaned</option>
                    <option value="Not Applicable">Not Applicable</option>
                    <option value="See Section Notes">See Notes</option>
                </select>
    <!-- Etc... -->

Could I use the title to narrow the selection? Or would the ID make more sense?


Solution

  • To answer your question: "Could I use the title to narrow the selection? Or would the ID make more sense?"

    A stable, numerical id is usually your best bet. Title, or other, text is subject to: editing or translation/internationalization. But if the id changes from page to page, or from page reload; don't use it.

    There are several problems with that code:

    1. waitForDropdown() is neither standard nor defined. Questions should contain MCVEs.
      In this case, it looks like a possible version of waitForKeyElements -- which is now common, standard, and battle tested.
    2. The selector: option[value='See Notes'] does not match any of the HTML.
      Refer to the jQuery Attribute selectors docs.
    3. jNode.val('See Notes'); is attempting to set an invalid value.
    4. jNode.val('See Notes'); is operating on a <div class="control">. It needs to operate on the <select>.

    Anyway, the companion question illustrates a top-down tree traversal to get the right nodes. So, here, I will illustrate a bottom up approach. It is also AJAX aware, unlike that other answer.
    Refer to the jQuery Tree Traversal docs.

    Here is a complete userscript that show how to discriminate options based on an associated node/id. (The userscript is just the first gray block):

    // ==UserScript==
    // @name     _Set <select> value under specific HTML section id
    // @match    *://YOUR_SERVER.COM/YOUR_PATH/*
    // @require  https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
    // @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
    // @grant    GM_addStyle
    // @grant    GM.getValue
    // ==/UserScript==
    //- The @grant directives are needed to restore the proper sandbox.
    
    waitForKeyElements (".control > select > option[value$='Cleaned']", selectId302Value);
    
    function selectId302Value (jNode) {
        //-- Make sure control belongs to the correct id:
        var idNode  = jNode.closest (".field-content").prev (".item-data");
        if (idNode.length === 0) {
            console.error ("Page structure changed or invalid in selectId302Value().");
            return;
        }
        if (idNode.text ().includes ('"302"') ) {
            var evt     = new Event ("click");
            jNode[0].dispatchEvent (evt);
    
            //-- Correct val already determined by WFKE selector.
            jNode.parent ().val(jNode.val () );
    
            //-- The select node would get any required change event.
            evt         = new Event ("change");
            jNode.parent ()[0].dispatchEvent (evt);
        }
    }
    <!----------------------------------------
    ----- Simulated target page follows: -----
    ----------------------------------------->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
    <script src="//greasyfork.org/scripts/2199-waitforkeyelements/code/waitForKeyElements.js"></script>
    <div class="field tabular">
        <span class="item-data">{"Id":"302"}</span>
        <div class="field-content">
            <div class="title" title="Dropdown A">Dropdown A</div>
            <div class="data">
                <div class="errors"></div>
                <div class="control">
                    <select>
                        <option value="Not Checked" selected="selected">Not Checked</option>
                        <option value="Checked &amp; Cleaned">Checked &amp; Cleaned</option>
                        <option value="Not Applicable">Not Applicable</option>
                        <option value="See Section Notes">See Notes</option>
                    </select>
        </div></div></div>
    
        <span class="item-data">{"Id":"777"}</span>
        <div class="field-content">
            <div class="title" title="Dropdown B">Dropdown B</div>
            <div class="data">
                <div class="errors"></div>
                <div class="control">
                    <select>
                        <option value="Not Checked" selected="selected">Not Checked</option>
                        <option value="Checked &amp; Cleaned">Checked &amp; Cleaned</option>
                        <option value="Not Applicable">Not Applicable</option>
                        <option value="See Section Notes">See Notes</option>
                    </select>
        </div></div></div>
    </div>

    Run the code snippet to see it in action.