Skip to content
Advertisement

javascript choose one element at a time

I have a code below

var border_3 = document.getElementById("premium__3");
var clicked_3 = false;
var border_6 = document.getElementById("premium__6");
var clicked_6 = false;
var border_12 = document.getElementById("premium__12");
var clicked_12 = false;


function validation() {
  var username = document.getElementById("username_input");
  var errorsCount = 0;

  if (username.value == "") {
    username.style.boxShadow = "#f04747 0px 0px 0px 1px";
    errorsCount = errorsCount + 1;
  } else {
    username.style.boxShadow = null;
  }
  if (clicked_3 == false) {
    border_3.style.boxShadow = "#f04747 0px 0px 0px 1px";
  }
  if (clicked_6 == false) {
    border_6.style.boxShadow = "#f04747 0px 0px 0px 1px";
  }
  if (clicked_12 == false) {
    border_12.style.boxShadow = "#f04747 0px 0px 0px 1px";
  }
  if(clicked_3 == true)
  {
  premium_3();
  }
  if(clicked_6 == true)
  {
  premium_6();
  }
  if(clicked_12 == true)
  {
  premium_12();
  }


}

function premium_3() {
  border_3.style.boxShadow = "#7289da 0px 0px 0px 1px";
  border_6.style.boxShadow = null;
  border_12.style.boxShadow = null;
  clicked_3 = true;
  clicked_6 = false;
  clicked_12 = false;

}

function premium_6() {
  border_6.style.boxShadow = "#7289da 0px 0px 0px 1px";
  border_3.style.boxShadow = null;
  border_12.style.boxShadow = null;
  clicked_6 = true;
  clicked_3 = false;
  clicked_12 = false;

}

function premium_12() {
  border_12.style.boxShadow = "#7289da 0px 0px 0px 1px";
  border_3.style.boxShadow = null;
  border_6.style.boxShadow = null;
  clicked_12 = true;
  clicked_3 = false;
  clicked_6 = false;

}
<div onclick="premium_3()" id="premium__3" class="months_3">
  <img src="" style="width: 100px;">
  <div class="title_3">3 MONTHS</div>
</div>

<div onclick="premium_6()" id="premium__6" class="months_6">
  <img src="" style="width: 100px;">
  <div class="title_6">6 MONTHS</div>
</div>

<div onclick="premium_12()" id="premium__12" class="months_12">
  <img src="" style="width: 100px;">
  <div class="title_12">12 MONTHS</div>
</div>

<div class="accept_nitro">
  <button type="button" class="step1_nitro_button" onclick="validation()">
      Next
    </button>
</div>

When I click on a button it first performs validation, it checks if the element was clicked (there are 3 elements in total), if not, it changes border color to red( hex color #f04747). The problem is when I choose one element the other two change color to red but It shouldn’t do that, because I already chose.

Advertisement

Answer

If you want to validate the options, you need to check if at least one is selected. If one is selected, then it is valid, and you can clear the others. You can pass the this reference into a singular function that handles checking if the options being iterated over matches the intercepted element.

I modified the premium option variables by storing them inside of a data structure. This males it east to traverse the options. There is still more work you can do to this. For instance, you can eliminate the clicked variables and just modify the dataset or data-attributes” of the elements. I sort of accomplished this via: curr.el.dataset.matches = matches.

Example

  1. Click “Next”
  2. Two errors should have occurred (text input and the premium options)
  3. Select a premium option
  4. Click “Next”
  5. Only one error is present now (text input)

const errorBoxShadow = "#f04747 0px 0px 0px 1px";

const premiumOptions = [{
  el: document.getElementById("premium__3"),
  clicked: false
}, {
  el: document.getElementById("premium__6"),
  clicked: false
}, {
  el: document.getElementById("premium__12"),
  clicked: false
}];

function validation() {
  const username = document.getElementById("username_input");
  const usernamePresent = username.value.length > 0;
  let errorsCount = 0;

  username.style.boxShadow = !usernamePresent ? errorBoxShadow : null;

  if (!usernamePresent) {
    errorsCount += 1;
  }

  // No selection...
  if (!premiumOptions.some(option => option.clicked)) {
    premiumOptions.forEach(option => option.el.style.boxShadow = errorBoxShadow);
    errorsCount += 1;
  }
  
  const errs = document.querySelector('#errors');
  errs.querySelector('.error-count').textContent = errorsCount;
  errs.classList.toggle('errors-present', errorsCount > 0);
  
  return errorsCount === 0; // Valid state
}

function onPremiumSelect(option) {
  premiumOptions.forEach(curr => {
    const matches = curr.el === option;
    curr.clicked = matches;
    curr.el.style.boxShadow = null;
    curr.el.dataset.matches = matches;
  });
}
div[class^="months_"] {
  display: inline-block;
  border: thin solid grey;
  padding: 0.5em;
  margin: 1em 0.25em;
}

div[class^="months_"]:hover {
  cursor: pointer;
}

div[class^="months_"][data-matches="true"] {
  background: #DD8;
}

#errors {
  display: none;
  color: red;
  margin-bottom: 0.5em;
}

#errors.errors-present {
  display: block;
}
<input type="text" id="username_input" />

<br />

<div onclick="onPremiumSelect(this)" id="premium__3" class="months_3">
  <img src="" style="width: 100px;">
  <div class="title_3">3 MONTHS</div>
</div>

<div onclick="onPremiumSelect(this)" id="premium__6" class="months_6">
  <img src="" style="width: 100px;">
  <div class="title_6">6 MONTHS</div>
</div>

<div onclick="onPremiumSelect(this)" id="premium__12" class="months_12">
  <img src="" style="width: 100px;">
  <div class="title_12">12 MONTHS</div>
</div>

<div id="errors">
  Errors: <span class="error-count"></span>
</div>

<div class="accept_nitro">
  <button class="step1_nitro_button" onclick="validation()">Next</button>
</div>

A better way

A better way to handle validation would be to have a hidden field that gets updated when you change the tier selection. Try to use as much of the built-in <form> as possible, and don’t forget about the required input attribute.

Update: To properly submit the form, make sure the <button> is type="submit" and the <form> is novalidate="true" and properly return true/false for your validation.

const form = document.forms['subscription'];

const extractFormData = form =>
  [...form.elements]
    .filter(el => el.name)
    .reduce((acc, el) => ({ ...acc, [el.name]: el.value }), {});

const validateAndSubmit = e => {
  // Toggle invalid state...
  const invalidElements = [...e.target.elements].filter(el => {
    const valid = !el.hasAttribute('required') ||
      (el.hasAttribute('required') && el.value.trim().length > 0);
      
    if (el.getAttribute('type') === 'hidden' && el.dataset.controls) {
      document.querySelectorAll(el.dataset.controls).forEach(controlled =>
        controlled.classList.toggle('invalid', !valid));
    } else {
      el.classList.toggle('invalid', !valid);
    }
    return !valid;
  });
  
  const
    formData = extractFormData(e.target),
    isFormValid = invalidElements.length === 0;
  
  console.log([
    `Form Data: ${JSON.stringify(formData)}`,
    `Valid: ${isFormValid}`
  ].join(' | '));
  
  if (!isFormValid) {
    e.preventDefault(); // Do not allow submission.
    return false;
  }
  
  return true; // Will navigate away! (success!)
};

const updateTierSelection = (activeTier) => {
  const valueChangedAndNotEmpty = activeTier.dataset.value !== form.elements.tier.value &&
    activeTier.dataset.value.length > 0;
  document.querySelectorAll('.tier').forEach(tier => {
    if (tier === activeTier) {
      if (tier.dataset.selected) {
        delete tier.dataset.selected;
        form.elements.tier.value = null;
      } else {
        tier.dataset.selected = true;
        form.elements.tier.value = tier.dataset.value;
      }
    } else {
      delete tier.dataset.selected;
    }
    if (valueChangedAndNotEmpty) {
      tier.classList.remove('invalid');
    }
  });
};

const handleTierChange = e => {
  if (e.target.classList.contains('tier')) {
    updateTierSelection(e.target);
  }
};

document.querySelector('.tiers').addEventListener('click', handleTierChange);
form.addEventListener('submit', validateAndSubmit);
.as-console-wrapper { max-height: 4em !important; }

.tiers {
  display: flex;
  flex-direction: row;
  margin: 0.5em 0;
}

.tier {
  border: thin solid grey;
  margin-right: 0.5em;
  padding: 0.5em;
}

.tier:hover {
  cursor: pointer;
}

.tier[data-selected="true"] {
  background: #DD6;
}

.invalid {
  border-color: red;
}
<form name="subscription" novalidate="true">
  <input type="text" name="username" required />
  <div class="tiers">
    <div class="tier" data-value="3">3 Months</div>
    <div class="tier" data-value="6">6 Months</div>
    <div class="tier" data-value="12">12 Months</div>
    <input type="hidden" name="tier" required data-controls=".tier" />
  </div>
  <button type="submit">Submit</button>
</form>
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement