Less variables and actions to hide input fields and to show divs

Viewed 86

This is my first time using Javascript, so please forgive me. I'm still not solid on the terminology or best practices, but I'm losing my mind with how complex this script is getting for such a simple thing.

Also if there's a better way, in general, to do what I'm trying to do, let me know because boy I have lost sleep on this one.

Context:

I have a form to build standardized email signatures. The user puts their info into the inputs and then copies the output from a copyable area with standardized styling specific for email markup. In each signature, someone may include the information for another person in their company.

I have a set of radio buttons that enable 0, 1, 2, or 3 additional fieldsets of input for these team members. In addition to adding fieldsets of inputs, they also enable outputs in the copyable area. The outputs have to be "display: none" so that someone who does not include this information in their signature doesn't end up with blank table cells in their copied signature.

https://jsfiddle.net/slingtruchoice/jrem21yb/

Here's the whole, ugly thing. I'm proud but also so not proud of it. Like I said, literally the first time I've ever used javascript. Specifically, I'm looking at this:

var radios = document.getElementsByName('addTeam');
for (var i = 0; i < radios.length; i++) {
  radios[i].addEventListener('change', function() {
//--------------------------------- || RADIO BUTTONS || -------------------------------------------
    let fieldset = document.getElementsByClassName('sigform-fieldset'), //CLASS fieldset for all radio buttons
        inputs = document.getElementsByClassName('sigform-team'), //CLASS all radio buttons
        izero = document.getElementById('add-team__0'), //ID radio button, input 0
        ione = document.getElementById('add-team__1'), //ID radio button, input 1
        itwo = document.getElementById('add-team__2'), //ID radio button, input 2
        ithree = document.getElementById('add-team__3'); //ID radio button, input 3
//--------------------------------- || INPUT SECTIONS || -------------------------------------------
    let divs = document.getElementsByClassName('sigform__team-inputs'), //CLASS all input wrapper divs
        done = document.getElementById('team-inputs__1'), //ID div of input section, team 1
        dtwo = document.getElementById('team-inputs__2'), //ID div of input section, team 2
        dthree = document.getElementById('team-inputs__3'); //ID div of input section, team 3
//--------------------------------- || SIGNATURE OUTPUT || -------------------------------------------
    let // ------------------------       Table Rows       -------------------------------------------
        teamsrows = document.getElementsByClassName('extraTeamWrap'), //CLASS of tr wrap each output table
        teamwrap1 = document.getElementById('extraTeamWrap1'), //ID tr wrap of output table team 1
        teamwrap2 = document.getElementById('extraTeamWrap2'), //ID tr wrap of output table team 2
        teamwrap3 = document.getElementById('extraTeamWrap3'), //ID tr wrap of output table team 3
        // ------------------------         Tables         -------------------------------------------
        teamtables = document.getElementsByClassName('extraTeamTable'), //CLASS of table for each output
        teamtable1 = document.getElementById('extraTeamTable-one'), // ID table wrap of output table team 1
        teamtable2 = document.getElementById('extraTeamTable-two'), // ID table wrap of output table team 2
        teamtable3 = document.getElementById('extraTeamTable-three'); // ID table wrap of output table team 3
    if (ione.checked == false && itwo.checked == false && ithree.checked == false || izero.checked == true){
      done.style.display = 'none';
      dtwo.style.display = 'none';
      dthree.style.display = 'none';
      teamsrows.style.display = 'none';
    } else if (ione.checked == true && itwo.checked == false && ithree.checked == false) {
      done.style.display = 'block';
      teamsrows.style.display = "block";
      dtwo.style.display = 'none';
      dthree.style.display = 'none';
    } else if (ione.checked == false && itwo.checked == true && ithree.checked == false) {
      done.style.display = 'block';
      dtwo.style.display = 'block';
      dthree.style.display = 'none';
    } else if (ione.checked == false && itwo.checked == false && ithree.checked == true) {
      done.style.display = 'block';
      dtwo.style.display = 'block';
      dthree.style.display = 'block';
    } else {
      return false;
    }
  });
}

And it's not even done. (Oh yeah, by the way, please don't expect this fiddle to work. It's far from there.)

How do I go about this better? I'm having difficulty googling answers for my questions since I don't really know how to say "how to make an argument equal to multiple IDs that are paired specifically with other IDs to do something to that ID when the other ID is activated... +javascript" in a way that yields useful results.

My only request is that any explanation come in very simple language. I really can't iterate enough how much of an absolute beginner I am. Most responses on StackExchange I've found for other questions have just blown right over my head.

And really, thank you for any help you're able to give!

2 Answers

Well, that took more than a minute. Here is the updated code. Are there more efficient ways of doing this? Probably. Here, I want you take away how data structures can simplify your code.

let radios = document.getElementsByName('addTeam');
for (let i = 0; i < radios.length; i++) {
  radios[i].addEventListener('change', function() {

//Based on whether it is a class or an id
const elementClasses = [
    'sigform-fieldset',
    'sigform-team',
    'sigform__team-inputs',
    'extraTeamWrap',
    'extraTeamTable',
];

const elementIds = [
    'add-team__0',
    'add-team__1',
    'add-team__2',
    'add-team__3',
    'team-inputs__1',
    'team-inputs__2',
    'team-inputs__3',
    'extraTeamWrap1',
    'extraTeamWrap2',
    'extraTeamWrap3',
    'extraTeamTable-one',
    'extraTeamTable-two',
    'extraTeamTable-three'
];

    //Adding elements to the loop
    elementsTotal = [];

    //For each loop to iterate the array
    elementClasses.forEach(element => {
        push(document.getElementsByClassName(element));
    });

    elementIds.forEach(element => {
        push(document.getElementsByClassName(element));
    });

    //The commented code is for classes. 
    //document.getElementsByClassName returns a collection, so you must iterate that collection to style.

    if (elementsTotal[6].checked == false && elementsTotal[7].checked == false && elementsTotal[8].checked == false || elementsTotal[5].checked == true){
        elementsTotal[9].style.display = 'none';
        elementsTotal[10].style.display = 'none';
        elementsTotal[11].style.display = 'none';
      //teamsrows.style.display = 'none';
    } else if (elementsTotal[6].checked == true) { 
        elementsTotal[9].style.display = 'block';
      //teamsrows.style.display = "block";
        elementsTotal[10].style.display = 'none';
        elementsTotal[11].style.display = 'none';
    } else if (elementsTotal[7].checked == true) {
        elementsTotal[9].style.display = 'block';
        elementsTotal[10].style.display = 'block';
        elementsTotal[11].style.display = 'none';
    } else if (elementsTotal[8].checked == true) {
        elementsTotal[9].style.display = 'block';
        elementsTotal[10].style.display = 'block';
        elementsTotal[11].style.display = 'block';
    } else {
      return false;
    }
  });
}

So I talked about objects, but I can't really see how it might be implemented here. Thus, I would like to show an example of how it might be used.

Example from: https://betterprogramming.pub/stop-putting-so-many-if-statements-in-your-javascript-3b65aaa4b86b

Here, is a pretty simple if statement approach, not the most clean code.

function getStatusColor (status) {
  if (status === 'success') {
    return 'green'
  }
  if (status === 'warning') {
    return 'yellow'
  }
  if (status === 'info') {
    return 'blue'
  }
  if (status === 'error') {
    return 'red'
  }
}

Better code:

function getStatusColor (status) {
  return {
    success: 'green',
    warning: 'yellow',
    info: 'blue',
    error: 'red'
  }[status]
}

With an object, it is easier to tell what is happening. I couldn't fit an object anywhere in your code as the if statements had booleans instead of strings. (I merely thought about strings, I wish I knew how to do it with booleans, though!)

Again, use data structures! I hope you learned some things, I did too.

Pretty impressive starter project and results! I am also learning Javascript and the most important thing for me are speaking and short variable/function names. If you can still understand it after 3 or 6 months, it's good code :)

Maybe an unexpected answer, but at least from my perspective some repetition is OK. Especially, for this "smaller" project and because you just started coding in Javascript. You can still tweak your code and remove repetitions later.

The "refactored" code still cannot write to the extra fields to the right but now it:

  • adds the extra fields (at the bottom and to the right) depending on your team checkboxes.
  • uses shorter and more speaking variable names, incl. all HTML elements (repeating the word "sigform" may be unnecessary).

For a loop to add an eventlistener to all checkboxes, check out:

Regarding the code repetition, check out:

Let me know if you have any questions or issues to understand my changes. I hope this helps.

const phonePrinter = function() {
  // using more speaking names here, can still be improved
  let phoneNumberDir = document.querySelector('#input__phone__dir').value.replace(/\D/g, "");
  let directPhone = document.querySelector('#phone-output__dir');
  let officePhone = document.querySelector('#input__phone__off').value.replace(/\D/g, "");
  let extension = document.querySelector('#input__ext__off').value;
  let officePhoneWithExtension = officePhone + ',' + extension; 
  let phoneLinkOff = document.querySelector('#phone-output__off');
    
  // the input is a phone number
  function validatePhoneNumber(phoneNumber) {
    var re = /^\(?(\d{3})\)?[- ]?[. ]?(\d{3})[- ]?[. ]?(\d{4})$/;
    return re.test(phoneNumber);
    return phoneNumber
  }

  // let's use only if statemetns for clarity, you return directly anyway
  const noPhoneNumber = officePhone == '' && phoneNumberDir == ''
  if (noPhoneNumber) {
    alert("Enter at least one phone number.")
    return
  }
  if (!validatePhoneNumber(officePhone)) {
    alert("Enter a valid office phone number.")
    return
  }
  if (!validatePhoneNumber(phoneNumberDir)) {
    alert("Enter a valid direct phone number.")
    return
  }

  //populating the phone in the href
  directPhone.setAttribute("href", "tel:" + phoneNumberDir); 
  directPhone.innerHTML = addDots(phoneNumberDir);

  //populating the phone + ext in the href
  phoneLinkOff.setAttribute("href", "tel:" + officePhoneWithExtension); 
  phoneLinkOff.innerHTML = addDots(phoneNumberDir, extension);
  
  event.preventDefault();
};

// function to handle the dots in the phone number, externalNumber is a default parameter, not set by default
function addDots(phoneNumber, externalNumber='') {
    var number = phoneNumber.slice(0, 3) + "." + phoneNumber.slice(3, 6) + "." + phoneNumber.slice(6, 10);
    
    // if external number is set, add it to the number output
    if (externalNumber != '') { return number + " ext " + externalNumber }
    return number
}

// this should be much clearer now. New order and shorter names
function printName() {
    var name = document.querySelector('#input__name').value;
        var title = document.querySelector('#input__title').value;
    if (name == '') alert("Please enter your name.");
    if (title == '') alert('Please enter your title.');
    
    if (name != '' && title != '') {
      const nameOutput = document.querySelector('#nameOutput');
      const titleOutput = document.querySelector('#titleOutput');
      nameOutput.innerHTML = name;
      titleOutput.innerHTML = title;
    }
}

// removed the uncommented code here, just for testing
// checkLimiter = document.getElementsByTagName('banner-checks'); ... 

var checkbox = document.querySelectorAll(".check");

for (var i = 0; i < checkbox.length; i++)
  checkbox[i].onclick = selectiveCheck;

function selectiveCheck(event) {
  var checked = document.querySelectorAll(".check:checked");
  // only used here, updated the name and use > maxBanners below instead of = and +1
  const maxBanners = 2
  if (checked.length > maxBanners) {
    alert("You may only have up to two additional banners in your signature.");
    return false;
  }
}

// not required to set default hidden in your html 
// the classList.add("hidden") will hide the elements
// below, we will remove the hidden class using remove to make it visible again
var teamFields1 = document.getElementById('team-inputs__1');
var teamFields2 = document.getElementById('team-inputs__2');
var teamFields3 = document.getElementById('team-inputs__3');
teamFields1.classList.add("hidden");
teamFields2.classList.add("hidden");
teamFields3.classList.add("hidden");

// name maybe still not optimal
var table1 = document.getElementById('teamTable1');
var table2 = document.getElementById('teamTable2');
var table3 = document.getElementById('teamTable3');
table1.classList.add("hidden");
table2.classList.add("hidden");
table3.classList.add("hidden");

// unsure how to handle/improve this right now, see link in answer
var radios = document.getElementsByName('addTeam');
for (let i = 0; i < radios.length; i++) {
  radios[i].addEventListener('click', function() {
    
    // removed some variables, we still have repetition
    const input0 = document.getElementById('add-team__0');
    const input1 = document.getElementById('add-team__1');
    const input2 = document.getElementById('add-team__2');
    const input3 = document.getElementById('add-team__3');

    if (input0.checked == true){                
      table1.classList.add('hidden');
      table2.classList.add('hidden');
      table3.classList.add('hidden');
      
      teamFields1.classList.add("hidden");
      teamFields2.classList.add("hidden");
      teamFields3.classList.add("hidden");

    } else if (input1.checked == true) {
    
      table1.classList.remove('hidden');
      table2.classList.add('hidden');
      table3.classList.add('hidden');
      
      teamFields1.classList.remove("hidden");
      teamFields2.classList.add("hidden");
      teamFields3.classList.add("hidden");

    } else if (input2.checked == true) {
    
      table1.classList.remove('hidden');
      table2.classList.remove('hidden');
      table3.classList.add('hidden');
      
      teamFields1.classList.remove("hidden");
      teamFields2.classList.remove("hidden");
      teamFields3.classList.add("hidden");
      
    } else if (input3.checked == true) {
    
      table1.classList.remove('hidden');
      table2.classList.remove('hidden');
      table3.classList.remove('hidden');
      
      teamFields1.classList.remove("hidden");
      teamFields2.classList.remove("hidden");
      teamFields3.classList.remove("hidden");
    }
  });
}

function populate() {
  phonePrinter();
  printName();
}
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap');

body {

  font-size: 16;
  font-family: "Source Sans Pro", Arial, sans-serif;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  background-color: #095285;
}

.columns {
  display: flex;
  flex-direction: row;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0.4) 100%);
}

form {
  padding: 5vw;
  width: 40vw;
  margin: 30px auto;
  display: flex;
  flex-direction: column;
}

.input {
  display: block;
  padding: 10px 10px 5px 0;
  width: 100%;
  box-sizing: border-box;
  margin: 10px;
  background-color: transparent;
  border-top: none;
  border-left: none;
  border-right: none;
  border-bottom: 2px solid #095285;
  font-size: 24px;
  font-weight: 600;
  font-family: "Source Sans Pro", Arial, sans-serif;
  color: #095285;
}

.input::placeholder {
  font-weight: 400;
  font-size: 24px;
  color: #095285;
  font-family: "Source Sans Pro", Arial, sans-serif;
  opacity: 1;
  text-transform: uppercase;
}

.output {
  width: 40vw;
  padding: 5vw;
  margin: 30px auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.output > .table-wrapper {
  font-family: "Source Sans Pro", sans-serif;
  color: #095285;
  font-size: 16px;
  background-color: white;
  border: 2px solid #095285;
  padding: 2vw;
    min-width: 375px;
}

.instructions {
  font-size: 24px;
  font-weight: bolder;
  text-align: center;
  color: #095285;
}

.no-select::selection {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  cursor: no-drop;
}

.selectable::selection,
table.selectable>*::selection {
  -webkit-touch-callout: auto;
  -webkit-user-select: auto;
  -khtml-user-select: auto;
  -moz-user-select: auto;
  -ms-user-select: auto;
  user-select: auto;
  cursor: copy !important;
}

/* banners, checkboxes */

/* checkboxes */

.dropdown-hidden {
  display: none;
}

.dropdown-visible {
  display: block;
}

/* banners */

.banner-hidden {
  display: none;
}

.banner-visible {
  display: block;
}

.visible {
    display: block;
}

.hidden {
  display: none;
}
<div class="columns">
  <!-- the only place where I kept sigform in the name. Other refs have been removed for shorter names --> 
  <form class="sigform" id="sigform" name="sigform">
    <input class="input" id="input__name" type="text" placeholder="Name:">
    <input class="input" id="input__title" type="text" placeholder="Title:">
    <input class="input" id="input__phone__dir" type="tel" placeholder="Direct phone number:">
    <input class="input" id="input__phone__off" type="tel" placeholder="Office phone number:">
    <input class="input" id="input__ext__off" type="number" placeholder="Office Extension:">
    <input class="input" id="input__website" type="url" placeholder="Team Website:">
    <label class="label" id="label__banner-inst" for="field__banner-fs">Add banners to your signature. You may choose up to 2.
      <fieldset class="fieldset" id="field__banner-fs" labeledby="label__banner-inst">
        <label class="label" id="label__banner-one">
          <input class="check" type="checkbox" name="banner-checks">
        </label>
        <label class="label" id="label__banner-two">
            <input class="check" type="checkbox" name="banner-checks">
        </label>
        <label class="label" id="label__banner-three">
            <input class="check" type="checkbox" name="banner-checks">
        </label>
        <label class="label" id="label__banner-four">
            <input class="check" type="checkbox" name="banner-checks">
        </label>
        <label class="label" id="label__banner-five">
            <input class="check" type="checkbox" name="banner-checks">
        </label>
        <label class="label" id="label__banner-six">
            <input class="check" type="checkbox" name="banner-checks">
        </label>
      </fieldset>
    </label>
    <label class="label" id="label__add-team">How many additional team members would you like represented in your signature?</label>
    <fieldset class="fieldset" id="field__add-team" labeledby="label__add-team">
      <label class="label" id="label__add-team__0" for="add-team__0">0
        <input class="team" id="add-team__0" type="radio" name="addTeam" value="0" labeledby="label__add-team__0" checked>
    </label>
      <label class="label" id="label__add-team__1" for="add-team__1">1
        <input class="team" id="add-team__1" type="radio" name="addTeam" value="1" labeledby="label__add-team__1">
    </label>
      <label class="label" id="label__add-team__2" for="add-team__2">2
        <input class="team" id="add-team__2" type="radio" name="addTeam" value="2" labeledby="label__add-team__2">
    </label>
      <label class="label" id="label__add-team__3" for="add-team__3">3
        <input class="team" id="add-team__3" type="radio" name="addTeam" value="3" labeledby="label__add-team__3">
    </label>
    </fieldset>
    <fieldset class="fieldset" id="field__team-sigs">
      <div class="team-inputs" id="team-inputs__1">
        <input type="text" id="team-1__name" class="input" placeholder="Team Member Name:">
        <input type="text" id="team-1__title" class="input" placeholder="Title:">
        <input type="text" id="team-1__email" class="input" placeholder="Email:">
        <input type="text" id="team-1__phone" class="input" placeholder="Direct Phone Number:">
      </div>
      <div class="team-inputs hidden" id="team-inputs__2">
        <input type="text" id="team-2__name" class="input" placeholder="Team Member Name:">
        <input type="text" id="team-2__title" class="input" placeholder="Title:">
        <input type="text" id="team-2__email" class="input" placeholder="Email:">
        <input type="text" id="team-2__phone" class="input" placeholder="Direct Phone Number:">
      </div>
      <div class="team-inputs start-hidden" id="team-inputs__3">
        <input type="text" id="team-3__name" class="input" placeholder="Team Member Name:">
        <input type="text" id="team-3__title" class="input" placeholder="Title:">
        <input type="text" id="team-3__email" class="input" placeholder="Email:">
        <input type="text" id="team-3__phone" class="input" placeholder="Direct Phone Number:">
      </div>
    </fieldset>
    <button id="submit" type="button" onClick="populate()">Submit</button>
    <img src="">

  </form>
  <div class="output">
    <p class="instructions no-select">
      HIGHLIGHT EVERYTHING BELOW TO COPY AND PASTE IN YOUR SIGNATURE
    </p>
    <div class="table-wrapper">

      <table class="signature selectable">
        <tr>
          <td colspan="1" id="nameOutput">Bobby Joe</td>
        </tr>
        <tr>
          <td colspan="1" id="titleOutput">Web Developer | Graphic Designer</td>
        </tr>
        <tr>
          <td colspan="1"><span id="phoneWrapper">Direct: <a href="tel:1112223333,8888" id="phone-output__dir">111.222.3333</a> | Office: <a href="#" id="phone-output__off">111.222.3333 ext. 123</a></span></td>

        </tr>
        <tr>
          <td colspan="1"><img src="https://via.placeholder.com/350x65">
          </td>
        </tr>
        <tr class="extraTeamWrap" id="extraTeamWrap1">
          <table class="extraTeamTable" id="teamTable1">
            <tr>
              <td class="extraTeam teamName" id="team-name__one">Extra Team member</td>
            </tr>
            <tr>
              <td class="extraTeam teamTitle" id="team-title__one">Extra Team title</td>
            </tr>
            <tr>
              <td class="extraTeam teamEmail" id="team-email__one"><a href="mailto:info@truchoicefinancial.com">Extra Team email</a></td>
            </tr>
            <tr>
              <td class="extraTeam teamPhone" id="team-phone__one"><a href="tel:1112223333">111.222.6666</a></td>
            </tr>
          </table>
        </tr>
        <tr class="extraTeamWrap" id="extraTeamWrap2">
          <table class="extraTeamTable" id="teamTable2">
            <tr>
              <td class="extraTeam teamName" id="team-name__two">Extra Team member</td>
            </tr>
            <tr>
              <td class="extraTeam teamTitle" id="team-title__two">Extra Team title</td>
            </tr>
            <tr>
              <td class="extraTeam teamEmail" id="team-email__two"><a href="mailto:info@truchoicefinancial.com">Extra Team email</a></td>
            </tr>
            <tr>
              <td class="extraTeam teamPhone" id="team-phone__two"><a href="tel:1112223333">111.222.6666</a></td>
            </tr>
          </table>
        </tr>
        <tr class="extraTeamWrap" id="extraTeamWrap3">
          <table class="extraTeamTable" id="teamTable3">
            <tr>
              <td class="extraTeam teamName" id="team-name__three">Extra Team member</td>
            </tr>
            <tr>
              <td class="extraTeam teamTitle" id="team-title__three">Extra Team title</td>
            </tr>
            <tr>
              <td class="extraTeam teamEmail" id="team-email__three"><a href="mailto:info@truchoicefinancial.com">Extra Team email</a></td>
            </tr>
            <tr>
              <td class="extraTeam teamPhone" id="team-phone__three"><a href="tel:1112223333">111.222.6666</a></td>
            </tr>
          </table>
        </tr>
      </table>
      <table class="signature selectable" id="banner-table">
        <tr class="tr__banner-opt banner-hidden" id="tr__banner-opt__one">
          <td class="td__banner-opt" id="td__banner-opt__one">Banner One</td>
        </tr>
        <tr class="tr__banner-opt banner-hidden" id="tr__banner-opt__two">
          <td class="td__banner-opt" id="td__banner-opt__two">Banner Two</td>
        </tr>
        <tr class="tr__banner-opt banner-hidden" id="tr__banner-opt__three">
          <td class="td__banner-opt" id="td__banner-opt__three">Banner Three</td>
        </tr>
        <tr class="tr__banner-opt banner-hidden" id="tr__banner-opt__four">
          <td class="td__banner-opt" id="td__banner-opt__four">Banner Four</td>
        </tr>
        <tr class="tr__banner-opt banner-hidden" id="tr__banner-opt__five">
          <td class="td__banner-opt" id="td__banner-opt__five">Banner Five</td>
        </tr>
        <tr class="tr__banner-opt banner-hidden" id="tr__banner-opt__six">
          <td class="td__banner-opt" id="td__banner-opt__six">Banner Six</td>
        </tr>
      </table>
    </div>
  </div>
</div>

Related