1

I have a select dropdown that on change I want it to run a function and pass along the select's value into that function. However, the function is expecting a variable holding an array, and what's being passed in is a string (the select's value).

You can see it working (or not working) in the code below. The selected value should represent its corresponding variable.

How can I accomplish this?

var str = ["Paul", "John", "Melissa", "Mike"];
var int = [8, 4, 55, 7];
var mix = ["Paul", 9, "John", 5, "Melissa", 67, "Mike", 2];

var main = document.querySelector('#main');
var sel = document.querySelector('select');


function testFunc(arr){
  arr.forEach((i) => {
    var p = document.createElement('p');
    p.innerHTML = i;
    main.appendChild(p);
  });
}

testFunc(mix);

sel.addEventListener('change', testFunc(sel.value));
<select>
  <option value="str">String</option>
  <option value="int">Integer</option>
  <option value="mix">Mixed</option>
</select>

<div id="main"></div>

EDIT:

I should note that the data arrays are coming in dynamically and I really would like to avoid manipulating them as to not cause additional code and confusion in future edits.

Sergio
  • 792
  • 3
  • 10
  • 35

5 Answers5

1

There are two issues in your code:

  1. In the event handler function, by specifying parenthesis after the function you are calling the function when the page loads. Just call the function inside of a function body. This will make the function invoked only when the event fires.

  2. sel.value is a string but the function expects an array. To evaluate the array dynamically change testFunc(mix) to testFunc('mix')(wrap the parameter value with quotes) and use window object like:

    arr = window[arr]
    

Replace:

sel.addEventListener('change', testFunc(sel.value));

With

sel.addEventListener('change', function (){testFunc([sel.value])});

var str = ["Paul", "John", "Melissa", "Mike"];
var int = [8, 4, 55, 7];
var mix = ["Paul", 9, "John", 5, "Melissa", 67, "Mike", 2];

var main = document.querySelector('#main');
var sel = document.querySelector('select');


function testFunc(arr){
  arr = window[arr]
  arr.forEach((i) => {
    var p = document.createElement('p');
    p.innerHTML = i;
    main.appendChild(p);
  });
}

testFunc('mix');

sel.addEventListener('change', function (){testFunc(sel.value)});
<select>
  <option value="str">String</option>
  <option value="int">Integer</option>
  <option value="mix">Mixed</option>
</select>

<div id="main"></div>
Mamun
  • 66,969
  • 9
  • 47
  • 59
  • this will cause the same error, you pass a string value and tries to iterate over it like an array. the clousure is useless – enno.void Jul 16 '18 at 17:25
  • oh im sorry, didn´t saw it. – enno.void Jul 16 '18 at 17:27
  • @Mamun Although your solution makes more sense to me, it did not work for me in my project...strange, cause I see it working on here. But Rajapandian's solution using eval() method worked me. – Sergio Jul 16 '18 at 17:38
  • This is not producing the required output (scroll down in the output). How is it possible this gets upvoted? – trincot Jul 16 '18 at 17:39
  • @Sergio, if the solution works here, there should not be any reason for which the code will not work in your project......may be should debug or console.log to see what is happening when the event fires............ – Mamun Jul 16 '18 at 17:49
  • @Mamun on second look, trincot is correct, this is not working correctly...it's just taking on the `sel.value` to the `dom` and not its corresponding array. – Sergio Jul 16 '18 at 17:52
  • @Sergio, one more thing, please go through before using `eval()`: https://stackoverflow.com/questions/86513/why-is-using-the-javascript-eval-function-a-bad-idea – Mamun Jul 16 '18 at 18:23
  • Using `arr = window[arr];` means they can only pass global variables. Why restrict the function like that? – Barmar Jul 16 '18 at 19:09
1

as i understand you want do to this:

let model = {
    str : ["Paul", "John", "Melissa", "Mike"];
    int : [8, 4, 55, 7];
    mix : ["Paul", 9, "John", 5, "Melissa", 67, "Mike", 2];
}


function testFunc(key){
    var arr = []
    if(model[key]){
        arr = model[key]
    }


    //TODO: clear parent node...
    arr.forEach((i) => {
        var p = document.createElement('p');
        p.innerHTML = i;
        main.appendChild(p);
    });
}
enno.void
  • 6,242
  • 4
  • 25
  • 42
  • Unfortunately the arrays that are being passed in I cannot manipulate...I mean I can, but I'd rather just use what's coming in as-is just to keep the code "cleaner". – Sergio Jul 16 '18 at 17:40
  • @Sergio Accessing variables dynamically is poor design, it limits how the function can be used. Replacing dynamic variables with objects is the correct approach. – Barmar Jul 16 '18 at 19:11
1
arr = eval(arr);

var str = ["Paul", "John", "Melissa", "Mike"];
var int = [8, 4, 55, 7];
var mix = ["Paul", 9, "John", 5, "Melissa", 67, "Mike", 2];

var main = document.querySelector('#main');
var sel = document.querySelector('select');


function testFunc(arr){
  arr = eval(arr);
  arr.forEach((i) => {
    var p = document.createElement('p');
    p.innerHTML = i;
    main.appendChild(p);
  });
}

testFunc(mix);

sel.addEventListener('change', function(){testFunc(this.value)});
<select>
  <option value="str">String</option>
  <option value="int">Integer</option>
  <option value="mix">Mixed</option>
</select>

<div id="main"></div>
Rajapandian
  • 216
  • 2
  • 7
  • `eval` should be avoided. It is really not necessary to make this work. – trincot Jul 16 '18 at 17:37
  • @trincot Could you explain as to why eval should be avoided. This actually worked for me, and I cannot manipulate the arrays being passed in...I could but I'd rather not. – Sergio Jul 16 '18 at 17:42
  • @trincot please let us know how to avoid eval. tried window[arr] but wasn't working . – Rajapandian Jul 16 '18 at 17:59
1

A few things to change:

  • Don't call the function that you pass to the addEventListener method. Instead pass a function reference. It can be an inline function expression. But don't call it.

  • Make your different arrays properties of one object variable, so you can easily reference them based on a value that is selected.

  • Clear the output whenever the selection is changed.

  • Trigger the initial change based on the item that is selected on page load.

Here is a working version:

var arr = {
    str: ["Paul", "John", "Melissa", "Mike"],
    int: [8, 4, 55, 7],
    mix: ["Paul", 9, "John", 5, "Melissa", 67, "Mike", 2]
};

var main = document.querySelector('#main');
var sel = document.querySelector('select');

function testFunc(arr){
  main.innerHTML = ""; // clear the previous result
  arr.forEach((i) => {
    var p = document.createElement('p');
    p.innerHTML = i;
    main.appendChild(p);
  });
}

sel.addEventListener('change', () => testFunc(arr[sel.value]));
testFunc(arr[sel.value]); // initial load
<select>
  <option value="str">String</option>
  <option value="int">Integer</option>
  <option value="mix">Mixed</option>
</select>

<div id="main"></div>

If your arrays are defined in the global scope and there is nothing you can do about that, then just copy references to them in your new object:

// Defined beforehand: you cannot change this:
var str = ["Paul", "John", "Melissa", "Mike"],
var int = [8, 4, 55, 7],
var mix = ["Paul", 9, "John", 5, "Melissa", 67, "Mike", 2]

// In your part of the code, still create array as in above solution:
var arr = {str: str, int: int, mix: mix};
// If you have ES6 support you can shorten the above to:
var arr = {str, int, mix};
trincot
  • 317,000
  • 35
  • 244
  • 286
  • trincot, your solution works, but how could I do it without manipulating the arrays? These are coming in dynamically and I really would like to avoid messing around with them as to not generate additional code/confusion. – Sergio Jul 16 '18 at 17:55
  • What do you mean with "coming in dynamically"? Can you be more concrete? – trincot Jul 16 '18 at 18:23
  • Anyway, I added some advise on how to deal with those global arrays: still put references to them in a wrapping object. – trincot Jul 16 '18 at 18:29
0

If I don't misunderstood your requirement then you've to do slight changes on your exiting JS variables and make it to Object. After that try like this way to append new paragraph elements on every change event of drop-down menu by removing the existing one with main.innerHTML=''

addEventListener: https://developer.mozilla.org/en-US/docs/Web/Events/change

Object Structure: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics

var data_types = {
  str: ["Paul", "John", "Melissa", "Mike"],
  int: [8, 4, 55, 7],
  mix: ["Paul", 9, "John", 5, "Melissa", 67, "Mike", 2]
}

var main = document.querySelector('#main');
var sel = document.querySelector('select');


function testFunc(event) {
   main.innerHTML = '';
  // You can use “this” to refer to the selected element.
  if (!event.target.value) alert('Please Select One');
  var arr = data_types[event.target.value];
  arr.forEach((i) => {
    var p = document.createElement('p');
    p.innerHTML = i;
    main.appendChild(p);
  });
}
  
document.addEventListener('DOMContentLoaded', function() {
  document.querySelector('select[name="data-types"]').onchange = testFunc;
}, false);
<select name="data-types">
  <option value="str">String</option>
  <option value="int">Integer</option>
  <option value="mix">Mixed</option>
</select>

<div id="main"></div>
A l w a y s S u n n y
  • 36,497
  • 8
  • 60
  • 103
  • Being Sunny, I would rather not manipulate the arrays and keep them as I listed initially. They are coming in dynamically and to avoid confusion and additional code, I rather they stay their own array/variable. – Sergio Jul 16 '18 at 18:01
  • @Sergio if it is coming dynamically then you can push them with key value on that object template. – A l w a y s S u n n y Jul 16 '18 at 18:07