Ben Buchanan
Ben Buchanan

Reputation: 5

Assign acceptable input based on a previous input

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Guees the Number</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="wrapper">
        <h1 class="header"> Guess the number</h1>
        <h3 class="info">Select your difficulty below</h3>
        <small>You have 10 guesses</small>
        <div class="settings">
            <div class="easy">
                <h1>Easy Mode</h1>
            </div>
            <div class="normal">
                <h1>Normal Mode</h1>
            </div>
            <div class="hard">
                <h1>Hard Mode</h1>
            </div>
            <div class="master">
                <h1>Master Mode</h1>
            </div>
        </div>

    <form action="" class="myForm">
    <input type="text" class="guess" placeholder="Enter your Guess here!">
    <input class="submit" vlaue="submit" type="submit"></input>

I'm making guessing games where the user can choose a difficulty mode that then changes the range of the random number generator ie( 0 to 25 0 to 50 etc.) Below is my attempt using if statements

<

easy.addEventListener('click', easyActive) // These are the difficulties
function easyActive() {
    easy.classList.add('active')
    normal.classList.remove('active')
    hard.classList.remove('active')
    master.classList.remove('active')
    const p = document.createElement('p')
    p.className = "para"
    p.innerText = 'Easy mode sets the range from 0 to 25'
    easy.append(p)
    if(p.innerText === 'Easy mode sets the range from 0 to 25') {
    random =  Math.floor(Math.random() * 26)  
    }
}

normal.addEventListener('click', normalActive)
function normalActive() {
    easy.classList.remove('active')
    normal.classList.add('active')
    hard.classList.remove('active')
    master.classList.remove('active')
    const p = document.createElement('p')
    p.className = "para"
    p.innerText = 'Normal mode sets the range from 0 to 50'
    normal.append(p)
    if(p.innerText === 'Normal mode sets the range from 0 to 50') {
        random =  Math.floor(Math.random() * 51)      
    }
}

hard.addEventListener('click', hardActive)
function hardActive() {
    easy.classList.remove('active')
    normal.classList.remove('active')
    hard.classList.add('active')
    master.classList.remove('active')
    const p = document.createElement('p')
    p.className = "para"
    p.innerText = 'Hard mode sets the range from 0 to 100'
    hard.append(p)
    if(p.innerText === 'Hard mode sets the range from 0 to 100') {
        random =  Math.floor(Math.random() * 101)      
    }
}

master.addEventListener('click', masterActive) 
function masterActive() {
    easy.classList.remove('active')
    normal.classList.remove('active')
    hard.classList.remove('active')
    master.classList.add('active')
    const p = document.createElement('p')
    p.className = "para"
    p.innerText = "Master mode sets the range from 0 to 200"
    master.append(p)
    random = Math.floor(Math.random() * 201)
}

myForm.addEventListener('submit', click)

function click(e) {
     e.preventDefault();
     const ul = document.querySelector('.guesses') // This is what i tried to do
     if(easy.className  == "active") {
         if(guess.value > 25 || guess.value < 0 || isNaN(guess.value)) {
           alert("Please Enter a Number between 0 and 25")
    }
  } else if(normal.className == "active") {
      if(guess.value > 50 || guess.value < 0 || isNaN(guess.value)) {
           alert("Please Enter a Number between 0 and 50")
    }
   } else if(hard.className == "active") {
       if(guess.value > 100 || guess.value < 0 || isNaN(guess.value)) {
           alert("Please Enter a number between 0 and 100")
    }
   } else if(master.className == "active") {
       if(guess.value > 200 || guess.value < 0 || isNaN(guess.value)) {
        alert("Please Enter a number between 0 and 200")
   }
 } else {
 
 // The rest is just the guessing game

. I want to make it so that when the user inputs their guess that it only accepts an input that is between the range of the difficulty chosen

Upvotes: 0

Views: 77

Answers (1)

Carsten Massmann
Carsten Massmann

Reputation: 28196

This fully functional snippet demonstrates what "DRY" (don't repeat yourself) really means. There are a number of points where repetition was avoided:

  • The quiz modes are defined in terms of an object (here:maxN). The magic then develops from there:

  • Part of the the HTML (selection of quiz mode) is generated by map()-ping over the keys of maxN.

  • Then, in the stgs.onclickfunction the actions to follow on any click on the four <input> elements in div.settings are defined - but only when an element el with el.tagName==="INPUT" is clicked on, will they unfold:

    • determine the upper boundary of the guessing range (mx=maxN[el.value]) for the chosen quiz mode (el.value),
    • set the guess.max=mx attribute for the input field receiving the guesses
    • delete any .textContent that might be found in resp
    • and generate a random number in the range of 0 ... mx
  • Hint: ~~() is a shorthand way of doing Math.floor():
    ~ is actually the "bitwise negation operator" that implictly applies a Math.floor() on the operand. By applying it twice the negation is reversed again.

The event handler for the form.submit event compares the guessed with the generated secret number and writes appropriate hints into #response.

I also chose to replace your four "mode" divs by radio buttons. These can be formatted through CSS in any way you like. Radio buttons have the built-in ability to allow only one of the same-name elements to be checked. So you won't have to take care of that in your JavaScript part.

DRY makes it easy to maintain your code, as you can see in the above example: In order to include the extra level "fiendish" I only need to add the extra property fiendish:500 to the maxN object and everything else falls into place!

By wrapping the whole thing in an "IIFE" all "global" constants and variables like number or the DOM-element references rng, stgs etc. are encapsulated in a temporary scope. This makes it harder for users to look for the "secret" number and it also keeps your global scope clean for further scripting operations.

(D => { // IIFE (immediately invoced functional expression) keeping the global scope "clean"
  // the following "destructing assignment" will assign DOM element references
  // to the four constants rng, resp, guess and stgs. D is just an alias for document.
  // .map() will take each of the strings in the array ["#range","#response",...] 
  // and use them as selectors in D.querySelector() calls returning 
  // the appropriate DOM element references.
  const [rng, resp, guess, stgs] = ["#range", "#response", ".guess", ".settings"]
  .map(el => D.querySelector(el)),

  // The object maxN has one property for each quiz level. 
  // Each key is the name of a level and the associated value 
  // is the upper limit of its number range.
    maxN = {
      easy: 25,
      normal: 50,
      hard: 100,
      master: 200,
      fiendish: 500
    };
  // the secret number to be guessed is in "global scope" within the IIFE
  var number;

  // generate the html-string to represent the radio buttons (with labels)
  // for the different quiz level's and assign it
  // to stgs.innerHTML:
  stgs.innerHTML = Object.keys(maxN).map(k =>
    `<label><input type="radio" name="level" value="${k}"> 
     ${k[0].toUpperCase()+k.slice(1)} Mode </label>`).join("");

  // define the onclick event handler for the #settings div:
  stgs.onclick = function(ev) {
    const el = ev.target; // el is the element that was clicked

    // Return without doing anything, if the event was NOT
    // triggered by a click on an <input> element.
    // (a click on a <label> element will implicitly
    // trigger a click event on the enclosed <input> element) 
    if (el.tagName !== "INPUT") return;

    // Ok, a quiz level was selected. Step into action:
 
   // reformat the labels: (go through all children of #settings and
    // set add/remove the class "selected" depending on whether 
    // the label has a checked input item in it or not)
    [...stgs.children].forEach(la=>
      la.classList.toggle("selected",la.querySelector("input:checked")));
    // get the upper limit of the number range: mx
    const mx = maxN[el.value];
    // set the upper limit for the number input field:
    guess.max = mx;
    // clear any text from the resp element:
    resp.textContent = "";
    // display the current number range:
    rng.textContent = `(0...${mx})`;
    // calculate the "secret" number:
    number = ~~(Math.random() * (mx + 1));
  }

  D.querySelector("form").addEventListener("submit", ev => {
    ev.preventDefault();
    let r = guess.value - number;
    resp.textContent = !r ? "You nailed it!" : r > 0 ? "No - Go lower." : "Not yet - you need to aim higher"
  })
})(document) // end of IIFE
label {
  display: block
}

.guess {
  width: 200px
}
[type=radio] {display:none}
.selected {font-weight:900;color:red}
<div class="wrapper">
  <h1 class="header"> Guess the number</h1>
  <h3 class="info">Select your difficulty below</h3>
  <div class="settings"></div><br>
  <small>You have 10 guesses <span id="range"></span></small>
  <form action="" class="myForm">
    <input type="number" class="guess" placeholder="Enter your Guess here!" min="0">
    <input class="submit" value="submit" type="submit">
  </form>
  <div id="response"></div>
</div>

Upvotes: 2

Related Questions