Rens van der Veldt
Rens van der Veldt

Reputation: 39

Javascript's getElementsByClassName() is unable to find proper classes

I've been trying to do a simple form setup with tabs using a guide from: https://www.w3schools.com/howto/howto_js_form_steps.asp.

I have three divs with class="tab" inside a form, which I am trying to make visible/invisible using javascript:

var currentTab = 0; // Current tab is set to be the first tab (0)
showTab(currentTab); // Display the current tab
function showTab(n) {
      // This function will display the specified tab of the form...
      var x = document.getElementsByClassName("tab");
      x[n].style.display = "block";
}

This is supposed to display the n'th tab but no tabs are visible when the page is loaded, moreover, both buttons (previous and next, see code below) are visible when the page is loaded and n = 0; I have tried to get this to work with different setups using:

<script> // code goes here </script>

or simply including a separate .js file, embedding the script section into, head, body, html or even the form has been unsuccessful. All the while, I can run simple js commands at the start of the same script section, like: alert("test");.

I am running the page on a Linux machine with a premade Bitnami LAMP stack.

<!Doctype html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="styles/login.css" />
    <link rel="stylesheet" href="styles/main.css">
    <link rel="stylesheet" href="styles/new_user.css">
    <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
    <script>

var currentTab = 0; // Current tab is set to be the first tab (0)
showTab(currentTab); // Display the current tab

function showTab(n) {

    // This function will display the specified tab of the form...
    var x = document.getElementsByClassName("tab");
    x[n].style.display = "block";

    //... and fix the Previous/Next buttons:
    if (n == 0) {
        document.getElementById("prevBtn").style.display = "none";
    } else {
        document.getElementById("prevBtn").style.display = "inline";
    }
    if (n == (x.length - 1)) {
        document.getElementById("nextBtn").innerHTML = "Submit";
    } else {
        document.getElementById("nextBtn").innerHTML = "Next";
    }
    //... and run a function that will display the correct step indicator:
    fixStepIndicator(n)
}

function nextPrev(n) {

    // This function should figure out which tab to display
    var x = document.getElementsByClassName("tab");

    // Hide the current tab:
    x[currentTab].style.display = "none";
    // Increase or decrease the current tab by 1:
    currentTab = currentTab + n;
    // if you have reached the end of the form... :
    if (currentTab >= x.length) {
    //...the form gets submitted:
        document.getElementById("register_form").submit();
        return false;
    }
    // Otherwise, display the correct tab:
    showTab(currentTab);
}

// This manages step indicators.
function fixStepIndicator(n) {
    // This function removes the "active" class of all steps...
    var i, x = document.getElementsByClassName("step");
    for (i = 0; i < x.length; i++) {
        x[i].className = x[i].className.replace(" active", "");
    }
    //... and adds the "active" class to the current step:
    x[n].className += " active";
}

</script>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body class="background">
    <main class="wrapper">
        <div id="parent">
            <form id="register_form" action="" method="post">
                <h1>New user:</h1>
                <div style="text-align:center;">
                    <span class="step"></span>
                    <span class="step"></span>
                    <span class="step"></span>
                </div>
                <div style="float:right; padding: 15px;">
                    <button type="button" id="nextBtn" onclick="nextPrev(1)">Next</button>
                </div>
                <div style="float:left; padding: 15px;">
                    <button type="button" id="prevBtn" onclick="nextPrev(-1)">Previous</button>
                </div>
                <div class="tab">
                        //content
                </div>

                <div class="tab">
                        //content
                </div>

                <div class="tab">
                       //content
                    </div>
                </div>
            </form>
        </div>
    </main>

</body>

</html>

The tab class itself looks like and should be invisible by default:

.tab {
  display: none;
}

I would like to find out why it is that the tabs in the form do not seem to be influenced by the showTab function. I've been at this issue for hours and I am absolutely clueless, any help would be appreciated!

Kind regards.

Upvotes: 0

Views: 2981

Answers (2)

some
some

Reputation: 49632

First problem with your code, is that the javascript is running before the page is loaded. Therefore it will not work. You must wait for the page to load before you call your function. There are several ways to to that, usually with a library.

In pure javascript you can listen for the event DOMContentLoaded on the document, something like this:

<script>
  var currentTab = 0; // Current tab is set to be the first tab (0)

  function showTab(n) {
      // This function will display the specified tab of the form...
      var x = document.getElementsByClassName("tab");
      x[n].style.display = "block";
  }

  document.addEventListener("DOMContentLoaded", function(event) {
    // this code will be running when the document is loaded
    showTab(currentTab); // Display the current tab

  });
</script>

Upvotes: 3

Intervalia
Intervalia

Reputation: 10975

I am not sure if this is 100% accurate, but the code below shows several improvements to your code.

I prefer querySelectorAll since I can pass in any CSS selector.

I used forEach to walk through the tabs and steps.

I only get the tabs once since that doesn't ever change.

But I wasn't 100% sure what the CSS did so I can't guarantee that this code is exactly what you need.

To make this work you must place the <script> after all your HTML. Place it as the last child in the <body>. don't call showTabs the first time until body.onload

var currentTab = 0; // Current tab is set to be the first tab (0)
var tabs = document.querySelectorAll(".tab");
showTab(currentTab); // Display the current tab

function showTab(n) {
  tabs.forEach(
    function(tab, i) {
      // This function will display the specified tab of the form...
      tab.style.display = n===i ? 'block' : 'none';
    }
  );

  //... and fix the Previous/Next buttons:
  if (n == 0) {
      document.getElementById("prevBtn").style.display = "none";
  } else {
      document.getElementById("prevBtn").style.display = "inline";
  }
  if (n == (tabs.length - 1)) {
      document.getElementById("nextBtn").innerHTML = "Submit";
  } else {
      document.getElementById("nextBtn").innerHTML = "Next";
  }
  //... and run a function that will display the correct step indicator:
  fixStepIndicator(n)
}

function nextPrev(n) {
  // Hide the current tab:
  tabs[currentTab].style.display = "none";
  // Increase or decrease the current tab by 1:
  currentTab = currentTab + n;
  // if you have reached the end of the form... :
  if (currentTab >= tabs.length) {
    //...the form gets submitted:
    document.getElementById("register_form").submit();
    return false;
  }

  // Otherwise, display the correct tab:
  showTab(currentTab);
}

// This manages step indicators.
function fixStepIndicator(n) {
  // This function removes the "active" class of all steps...
  var steps = document.querySelectorAll(".step");
  if (steps) {
    steps.forEach(
      function(step, i) {
        step.classList.toggle("active", n === i);
      }
    );
  }
}
<main class="wrapper">
    <div id="parent">
        <form id="register_form" action="" method="post">
            <h1>New user:</h1>
            <div style="text-align:center;">
                <span class="step">Step1</span>
                <span class="step">Step2</span>
                <span class="step">Step3</span>
            </div>
            <div style="float:right; padding: 15px;">
                <button type="button" id="nextBtn" onclick="nextPrev(1)">Next</button>
            </div>
            <div style="float:left; padding: 15px;">
                <button type="button" id="prevBtn" onclick="nextPrev(-1)">Previous</button>
            </div>
            <div class="tab">
                    //content 1
            </div>

            <div class="tab">
                    //content 2
            </div>

            <div class="tab">
                   //content 3
                </div>
            </div>
        </form>
    </div>
</main>

Upvotes: 0

Related Questions