Search code examples
javascripttampermonkeyuserscripts

Allow userscript function to be called from injected button then save with GM_saveValue


I have written a userscript for a particular webpage, and this script has some variables that I would like to be modifiable by the user, without them having to edit the script by hand such that the variables survive script updates.

Currently I am injecting a new <form> into the existing settings page of the website with the appropriate inputs and a <button> to trigger an injected function of which should run GM_saveValue with a value extracted from the form.

The problem is that this script/function isn't able to access GM_saveValue as that only exists in the scope of the TM script itself, hence I cannot actually save the settings. The rest of the code is fine, except this problem.

I have also tried defining the function inside of the userscript instead of injecting it in a <script> tag, but this has much the same problem that the page itself is unaware of this function due to it being in TM's scope.

Is there a way to either make GM_setValue accessible to the script that is injected into the page, or to make the button on the page able to access the function in the Userscript?

Option 1, inject a script tag:

    let custom_settings_section = document.createElement('section');
    let settings_script_tag = document.createElement('script');
    settings_script_tag.text = `
    function qol_saveCustomVariables() {
    GM_setValue("custom_currency", document.querySelector('qol_custom_currency').value); // GM_setValue is unavailable in this scope.
    document.querySelector('qol_save_success').style.display = "block";
    }
    `;
    main_content.appendChild(settings_script_tag);
    custom_settings_section.innerHTML = `
    </script>
    <h3>Ch4rl1e's QoL Settings</h3>
    <form onsubmit="return false">
    <p>Custom Currency Symbol: <input id="qol_custom_currency" type="string" name="qol_custom_currency" value=${custom_currency}></p>
    <button onclick=qol_saveCustomVariables()>Save</button>
    <p id="qol_save_success" class="alert alert-success" style="display: none;">Saved!</p>
    </form>
    `;

Option 2 define function in userscript:

    let main_content = document.querySelector("#rightcolumn");
    let custom_settings_section = document.createElement('section');
    function qol_saveCustomVariables() { // the button injected into the page cannot "see" this function
        GM_setValue("custom_currency", document.querySelector('qol_custom_currency').value);
        document.querySelector('qol_save_success').style.display = "block";
    }
    custom_settings_section.innerHTML = `
    <h3>Ch4rl1e's QoL Settings</h3>
    <form onsubmit="return false">
    <p>Custom Currency Symbol: <input id="qol_custom_currency" type="string" name="qol_custom_currency" value=${custom_currency}></p>
    <button onclick=qol_saveCustomVariables()>Save</button>
    <p id="qol_save_success" class="alert alert-success" style="display: none;">Saved!</p>
    </form>
    `

    main_content.appendChild(custom_settings_section);

Solution

  • Solved

    By creating the submit button "properly" (e.g. with document.createElement) and then registering an eventListener for "click", and putting the relevant functionality inside of that, it works!

        let main_content = document.querySelector("#rightcolumn");
        let custom_settings_section = document.createElement('section');
        custom_settings_section.innerHTML = `
        <h3 id="qol">Ch4rl1e's QoL Settings</h3>
        <form onsubmit="return false">
        <p id="qol_save_success" class="alert alert-success" style="display: none;">Saved!</p>
        <p>Custom Currency Symbol: <input id="qol_custom_currency" type="string" name="qol_custom_currency" value=${custom_currency}></p>
        </form>
        `;
        var custom_settings_save_button = document.createElement('button');
        custom_settings_save_button.class = "btn btn-primary";
        custom_settings_save_button.innerHTML = "Save";
        custom_settings_save_button.addEventListener("click", () => {
            GM_setValue("custom_currency", document.querySelector('#qol_custom_currency').value);
            document.querySelector('#qol_save_success').style.display = "block";
        });
        custom_settings_section.appendChild(custom_settings_save_button);
        main_content.appendChild(custom_settings_section);
    }
    

    (I did also move the div that reports a success message but it should be possible to insert the button between the form and said div, it's more work)