javascripthtmlgetelementbyidcoding-efficiency

Use variable in 'document.getElementById("…")' for IDs to reduce duplicated code


I have written a dice roller utility for a tabletop RPG in JavaScript.

Before I add more functionality to it, I would like to simplify the code by simplifying functions to remove unnecessary code.

When using the roller, some settings for rolls are changed by using menus. Each menu has its own function to pull the selected value into a global variable using document.getElementById.

There are 8 of these menus. But I need to add more menus to add more functionality. And I want to adapt this roller's code to make rollers for other games using the same system. (Names for IDs and variables would change.)

Instead of creating a new function for each menu, is it possible to pull a menu's ID into a variable that can be inserted into document.getElementById? For example, instead of:

MoonSettingSelected = document.getElementById("MoonPhaseMenu");

the code might read:

let SelectedSetting = document.getElementById("SelectedMenu");

I tried using QuerySelectorAll to pull data from the menus, but found it easier to use getElementById.

New to using JavaScript.

Sample code below is available on CodePen.

    <div class="centre RageOptions">
  <select id="MoonPhaseMenu" onclick="SetMoonPhase()" class="menu shadow">
    <option value="9999" selected>Moon phase</option>
    <option value="1">Full</option>
    <option value="2">Crescent</option>
    <option value="3">Half</option>
    <option value="4">Gibbous</option>
    <option value="5">New</option>
  </select>
</div>
<p>&nbsp</p>
<div>
  <div class="centre RageOptions">
    <select id="AuspiceMenu" onclick="SetAuspice()" class="menu shadow">
      <option value="9999" selected>Auspice</option>
      <option value="1">Ahroun</option>
      <option value="2">Theurge</option>
      <option value="3">Philodox</option>
      <option value="4">Galliard</option>
      <option value="5">Ragabash</option>
    </select>
  </div>
  <p>&nbsp</p>
  <div class="centre RageOptions">
    <select id="RankMenu" onclick="SetRank()" class="menu shadow">
      <option value="9999" selected>Rank</option>
      <option value="0">Cub (0)</option>
      <option value="1">Cliath (1)</option>
      <option value="2">Fostern (2)</option>
      <option value="3">Adren (3)</option>
      <option value="4">Athro (4)</option>
      <option value="5">Elder (5)</option>
      <option value="6">Legend (6)</option>
    </select>
  </div>
</div>
// CHANGE GLOBAL VARIABLES ON MENU SELECT

var MoonSetting = "9999";
var AuspiceSetting = "9999";
var GarouRank = Number(0);

function SetMoonPhase() {
  let MoonSettingSelected = document.getElementById("MoonPhaseMenu");
  MoonSettingSelected.addEventListener("change", function handleChange(event) {
    MoonSetting = Number(event.target.value);
  });
}

function SetAuspice() {
  AuspiceSettingSelected = document.getElementById("AuspiceMenu");
  AuspiceSettingSelected.addEventListener(
    "change",
    function handleChange(event) {
      AuspiceSetting = Number(event.target.value);
    }
  );
}

function SetRank() {
  // Set GarouRank global variable on menu change
  GarouRankSelected = document.getElementById("GarouRankMenu");
  GarouRankSelected.addEventListener("change", function handleChange(event) {
    GarouRank = Number(event.target.value);
  });
}

// …and repeat for 5 (and counting) functions, each of which is identical except for the ID in getElementById("…")

Solution

  • You can use helper function to attach eventListeners. (ids and variable names changed for convenience)

    const setMenuValue = (menuId) => {
      document.getElementById(menuId).addEventListener("change", ({target: {value}}) => {
        window[menuId] = Number(value);
        console.log(menuId, window[menuId])
      });
    };
    
    let MoonPhase = "9999";
    let Auspice = "9999";
    let Rank = 0;
    
    setMenuValue("MoonPhase");
    setMenuValue("Auspice");
    setMenuValue("Rank");
    .as-console-wrapper { max-height: 100% !important;  }
        <div class="centre RageOptions">
          <select id="MoonPhase" class="menu shadow">
            <option value="9999" selected>Moon phase</option>
            <option value="1">Full</option>
            <option value="2">Crescent</option>
            <option value="3">Half</option>
            <option value="4">Gibbous</option>
            <option value="5">New</option>
          </select>
        </div>
        <p>&nbsp</p>
        <div>
          <div class="centre RageOptions">
            <select id="Auspice" class="menu shadow">
              <option value="9999" selected>Auspice</option>
              <option value="1">Ahroun</option>
              <option value="2">Theurge</option>
              <option value="3">Philodox</option>
              <option value="4">Galliard</option>
              <option value="5">Ragabash</option>
            </select>
          </div>
          <p>&nbsp</p>
          <div class="centre RageOptions">
            <select id="Rank" class="menu shadow">
              <option value="9999" selected>Rank</option>
              <option value="0">Cub (0)</option>
              <option value="1">Cliath (1)</option>
              <option value="2">Fostern (2)</option>
              <option value="3">Adren (3)</option>
              <option value="4">Athro (4)</option>
              <option value="5">Elder (5)</option>
              <option value="6">Legend (6)</option>
            </select>
          </div>
        </div>