2

I have tried all the answers on StackOverflow. But, I can't seem to be able to programmatically select an option based off the value of the option.

I am doing this from a content script in a Chrome extension -> don't know if that changes anything.

My code is below:

chrome.runtime.onMessage.addListener(
  function(response, sender, sendResponse){
    if (typeof response === 'string' || response instanceof String){
        var dropdown = false;

        if ((document.getElementById(response)).nodeName == 'SELECT'){

            //console.log('im here');

            dropdown = true;
            var options = $('#' + response).find('option');
            var random_num = ~~((Math.random() * (options.length - 1)) + 1 );
            var radnom_option = options[random_num];
            var value = radnom_option.value;
            var slctr = ('#' + response);

            /*
              I thought that the next two lines would change the selected option.
            */
            $(slctr).val(value); 
            $(slctr).trigger('change.select2');

            console.log($(slctr));
            console.log(options);
            console.log(random_num);
            console.log(radnom_option);
            console.log(value);
            console.log(slctr);
        }

        dropdown = true;
        sendResponse(dropdown);
    }
});

When this runs, the console shows:

[select#entity_email_1_createPersonEmailType.form-control.entity_email.select2.required, context: document, selector: "#entity_email_1_createPersonEmailType"]

[option, option#option_business, option#option_other, option#option_personal, option#option_school, prevObject: init[1], context: document, selector: "#entity_email_1_createPersonEmailType option"]

2

<option id="option_other" data-option-id="option_other" value="916">Other</option>

916

#entity_email_1_createPersonEmailType

The part of the HTMl that I am trying to modify is this:

<div
                                        class="col-md-12"
                                                    >
                        <div class="form-group formGroup">
                                        <label
                            class="control-label"
                                                            for="entity_email[1][quickAddPersonEmailType]"
                                                            >Email Type* </label>
                                    <select
                        placeholder=""
                        class="form-control  entity_email select2  required "
                                                    name="entity_email[1][email_type]"
                                                    id="entity_email_1_quickAddPersonEmailType"
                        title="Email Type"
                                                    data-field-suffix=""
                        data-field-name="email_type"
                        data-init-callback=""
                        data-field-alias="Email Type"
                        tabindex=""
                                                                                 data-field-required="entity_email"                                                                                                         >
                                                                                                                                                            <option data-option-id="option_empty" value="">Select</option>
    <option
    id="option_business"
    data-option-id="option_business"
    value="915"
    >Business</option>
        <option
    id="option_other"
    data-option-id="option_other"
    value="916"
    >Other</option>
        <option
    id="option_personal"
    data-option-id="option_personal"
    value="913"
    >Personal</option>
        <option
    id="option_school"
    data-option-id="option_school"
    value="914"
    >School</option>

This is all what I would expect in terms of the console. But, still the select element remains at the first option, 'Select'. Also when I tried a method outlined on StackOverflow which included calling the select2() function, there was a TypeError saying that the select2() function was not defined. But how could this be the case, if the HTML includes select2 elements?

What am I doing wrong here? Is there another way to achieve the end goal of being able to select a specific option? For example, could I delete all the options, and then add them all again while changing the default?

Any help would be greatly appreciated. Thanks in advance.

  • 1
    Is this a typo? `radnom_option` – Will Aug 09 '16 at 23:55
  • `don't know if that changes anything` - use a [debugger](https://developers.google.com/web/tools/chrome-devtools/debug/breakpoints/add-breakpoints) - select Content scripts on the Sources devtools panel. – wOxxOm Aug 10 '16 at 00:04
  • Please [edit] your question to include the HTML on which you are attempting to perform changes. – Makyen Aug 10 '16 at 00:06
  • @will, It looks like a typeo, but `radnom_option` is used consistently throughout the code. The text `random_option` does not exist in the code. – Makyen Aug 10 '16 at 00:09
  • @Makyen, yeah, I know, that's why I worded it in the form of a question. :D – Will Aug 10 '16 at 00:17
  • @Will, sorry i don't really know what you mean - random_option is defined on line 13. -- Oh god I just saw it - yes, it is a typo but when I corrected all the spellings, the result does not change. – londonbatley Aug 10 '16 at 00:29
  • londonbatley, @Will 's issue is that the `d` and `n` are reversed (i.e. `radnom` instead of `random`) in `radnom_option`. – Makyen Aug 10 '16 at 00:34
  • @Makyen, sorry yes I just realized that - when I corrected the spelling the result didn't change, though. – londonbatley Aug 10 '16 at 00:35
  • londonbatley, yeah it is not a functionality issue, just a readability one. The reversal of `d` and `n` in that variable name was consistent throughout the code. For some strange reason :-), JavaScript does not care if you spell your own variable names correctly. It only cares that the variable names you use are consistent. – Makyen Aug 10 '16 at 00:38
  • Is the select inside a website? Here is an example implementation that will get the [Chrome Extension - Get DOM content](http://stackoverflow.com/questions/19758028/chrome-extension-get-dom-content), might help you solve your issue. – Mr.Rebot Aug 11 '16 at 07:50
  • I know this is more than a year old, but I'm trying to achieve the exact same thing, could you make any progress? – Ronald Rey Dec 03 '17 at 04:12

1 Answers1

0

I know it's been a long time, but I was having the same issue and managed to "solve" it, so just in case anyone else stumbles upon this...

The reason why the two lines you highlight don't change the value of the select, is because of the sandboxed nature of Chrome Extension's content scripts. Content scripts cannot access the page scope, so they can't reach any objects defined in it at all. This is an intentional behavior that Google included by design for security reasons, you can read more about it on the official documentation.

When you run the code that you included, the event triggered will not reach any event listeners declared in the page because the jQuery and select2 instance is not the same, so nothing happens, as you noticed.

If you absolutely need to, you can get around this obstacle by creating an <script> tag with an immediately invoked function expression (IIFE) that does the triggering, and insert it in the DOM.

Using JSX

const code = () => $('selector').val('some value').trigger('change.select2');
const script = <script>({code})();</script>;
document.body.appendChild(script);

If you don't have JSX available, you can always do it the old fashion way:

var code = () => $('selector').val('some value').trigger('change.select2');
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ code +')();'));
document.body.appendChild(script);

If you go this route, keep in mind that inside the function that you include in the <script> tag, you can't access variables from the content script's scope because it will be running in the scope of the page. Since this is your use case, I can see two ways of mitigating this,

  1. Put all of the code of the function inside a string, and pass that string as the contents of the script, something like...
var code = `() => $('${slctr}').val(${value});.trigger('change.select2');`;
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ code +')();'));
document.body.appendChild(script);
  1. Include the data that you need as data- attributes in the script, then in the function pull that data using something like script.getAttribute('data-selector'), for example, where script will be the inserted HTMLScriptElement (you will need a way to access the that node).
var code = () => {
    var script = document.getElementById('my-script-id');
    var selector = script.getAttribute('data-selector');
    var value = script.getAttribute('data-value');
    $(selector).val(value).trigger('change.select2');
};
var script = document.createElement('script');
script.id = 'my-script-id';
script.setAttribute('data-selector', selector);
script.setAttribute('data-value', value);
script.appendChild(document.createTextNode('('+ code +')();'));
document.body.appendChild(script);

This is terribly ugly and hacky, but there's really no other way around it.

I think that gets across the idea. Good luck.

Ronald Rey
  • 584
  • 7
  • 8