Javascript 到 Select 有限制的多个选项

Javascript to Select Multiple options with restrictions

我需要一个包含多个步骤的表单,其中第一步限制后续步骤中的选项。

下面是一个使用示例:

  1. 用户 select 来自 4 个全局选项

    • 如果用户 selects 选项 1
      • 然后用户只能select 8个选项(不多也不少)从一个多选框。
    • 如果用户select选项2
      • 那么用户只能从多选框中select10个选项(不多也不少)。
    • 选项3和4也是如此
  2. 完成所有这些 select 之后,此表格必须根据用户在第一步 select 中输入的选项显示最终价格。无论在连续的步骤中 select 做了什么选择。

  3. 当此表单显示最终价格时,用户必须单击“提交”按钮并通过电子邮件发送包含所有这些选项的结果。

有人可以帮我弄清楚执行此操作所需的 javascript 吗?现在对我来说最重要的事情是在多个项目选择框中找出 'restrictions codes'。

这是我正在考虑的形式:

<form action="" id="menuform" onsubmit="return false;">
    <fieldset>
        <legend>Select one of this four menus</legend>
        <label >Menu Option</label>
            <input type="radio"  name="selectedmenu" value="Menu1"
onclick="calculateTotal()" />
        Menu 1 -  serves 8 courses ()
            <input type="radio"  name="selectedmenu" value="Menu2"
onclick="calculateTotal()" />
        Menu 2 - serves 12 courses ()
            <input type="radio"  name="selectedmenu" value="Menu3"
onclick="calculateTotal()" />
        Menu 3 - serves 16 courses ()
            <input type="radio"  name="selectedmenu" value="Menu4"
onclick="calculateTotal()" />
        Menu 4 - serves 30 courses ()

        <label >Filling</label>
            <select id="filling" name='filling'
onchange="calculateTotal()">
                <option value="dish1">Dish 1 ()</option>
                <option value="dish2">Dish 2 ()</option>
                <option value="dish3">Dish 3 ()</option>
                (more...)
            </select>
        <br/>
        <div id="totalPrice"></div>
    </fieldset>
</form>

然后,对于 Javascript 代码,我尝试用它来存储和 return 一些值,但我的问题是如何强制 select 第二个选项中的确切数量步骤:

var menu_prices = new Array();
    menu_prices["Menu1"]=20;
    menu_prices["Menu2"]=25;
    menu_prices["Menu3"]=35;
    menu_prices["Menu4"]=75;

function getMenuPrice()
{
    var menuPrice=0;
    var theForm = document.forms["menuform"];
    var selectedMenu = theForm.elements["selectedmenu"];
    for(var i = 0; i < selectedMenu.length; i++)
    {
        if(selectedMenu[i].checked)
        {
            menuPrice = menu_prices[selectedMenu[i].value];
            break;
        }
    }
    return menuPrice;
}

function getTotal()
{
    var menuPrice = getMenuPrice() + getOtherOptionsPrices();

    document.getElementById('totalPrice').innerHTML =
                                      "Total Price For Menu $"+menuPrice;
}

仔细构建您的 HTML 标记,这有助于您轻松地通过 JavaScript 定位元素。特别是,在单选按钮上使用 data-attributes 来获取数量和价格,然后可以在相应的事件处理程序中轻松检索这些按钮。像这样:

<form action="" method="post" id="menuform" name="menuform">
    <fieldset>
        <legend>Select menu option</legend>
        <label>
            <input type="radio" name="selectedmenu" checked value="menu01" data-qty='3' data-price='20' />
            <span>1 -  serves 3 courses ()</span>
        </label>
        ...
    <fieldset>
        <legend id='fillingsPrompt'>Select fillings</legend>
        <select id="fillings" name="fillings" size="6" multiple>
            <option value="1">Dish 1 ()</option>
        ...
    <fieldset>
        <legend>Checkout</legend>
        <div id="totalPrice"></div>
        <input type="submit" value="Submit" />
    </fieldset>
    <fieldset>
        <legend>Messages</legend>
        <p id="result"></p>
    </fieldset>
</form>

确定并select您需要的所有元素:

var menuform        = document.getElementById('menuform'), 
    radios          = document.getElementsByName('selectedmenu'), 
    fillings        = document.getElementById('fillings'), 
    fillingsPrompt  = document.getElementById('fillingsPrompt'), 
    totalPrice      = document.getElementById('totalPrice'), 
    result          = document.getElementById('result'), 
    fillingsAllowed = 0, currentSelection = [], currency = '$'
;

将事件侦听器添加到您的单选按钮 select 和提交按钮:

menuform.addEventListener('submit', handleSubmit);
fillings.addEventListener('change', handleFillings);
for (var i = radios.length; i--; ) {
    radios[i].addEventListener('change', handleLimit);
}

编写实际的事件处理程序:

// When radio buttons are selected, update parameters for limit
function handleLimit(e) { updateParameters(e.target); }

// When options are selected in the dropdown, 
// check against the limit and reset the selection if it exceeds
function handleFillings(e) {
    var count = getSelectedCount();
    if (count > fillingsAllowed) { resetSelect(); }
    else { currentSelection = getSelectedValues(); }
}

// When submit button is clicked, 
// check the count of selection against the limit, and
// show appropriate error message
function handleSubmit(e) {
    var count = getSelectedCount();
    e.preventDefault();
    if (count != fillingsAllowed) { 
        result.textContent = 'Must select exactly ' + fillingsAllowed + ' fillings!';
    } else {
        result.textContent = 'Ok. ';
    }
}

然后对上述处理程序中使用的所有辅助函数进行编码:

function updateParameters(elem) {
    // update the limit based on quantity data attribute on radio
    fillingsAllowed = elem.getAttribute('data-qty');
    // show the amount based on price data-attribute
    totalPrice.textContent = 'Amount: ' + currency + elem.getAttribute('data-price');
    // show the hint on legend of fieldset for selecting options
    fillingsPrompt.textContent = 'Select ' + fillingsAllowed + ' fillings';
}

// iterate options and get count of selected ones
function getSelectedCount() {
    var options = fillings.options, count = 0;
    for (var i=0; i < options.length; i++) {
        if (options[i].selected) count++;
    }
    return count;
}

// iterate options and get selected values in an array
function getSelectedValues() {
    var options = fillings.options, values = [0];
    for (var i=0; i < options.length; i++) {
        if (options[i].selected) values.push(options[i].value);
    }
    return values;
}

// remove selection from all options, and 
// re-select based on the array used in the previous function
function resetSelect() {
    var options = fillings.options;
    for (var i=0; i < options.length; i++) {
        options[i].selected = false;
        if (currentSelection.indexOf(options[i].value) != -1) {
            options[i].selected = true;
        }
    }
}

所有内容放在一起,演示如下所示:

Fiddle: https://jsfiddle.net/abhitalks/L813qudw/

片段:

var menuform   = document.getElementById('menuform'), 
 radios    = document.getElementsByName('selectedmenu'), 
 fillings   = document.getElementById('fillings'), 
 fillingsPrompt  = document.getElementById('fillingsPrompt'), 
 totalPrice   = document.getElementById('totalPrice'), 
 result    = document.getElementById('result'), 
 fillingsAllowed = 0, currentSelection = [], currency = '$'
;

// listen to events 
menuform.addEventListener('submit', handleSubmit);
fillings.addEventListener('change', handleFillings);
for (var i = radios.length; i--; ) {
    radios[i].addEventListener('change', handleLimit);
}

// event handlers
function handleLimit(e) { updateParameters(e.target); }
function handleFillings(e) {
 var count = getSelectedCount();
 if (count > fillingsAllowed) { resetSelect(); }
 else { currentSelection = getSelectedValues(); }
}
function handleSubmit(e) {
 var count = getSelectedCount();
 e.preventDefault();
 if (count != fillingsAllowed) { 
  result.textContent = 'Must select exactly ' + fillingsAllowed + ' fillings!';
 } else {
  result.textContent = 'Ok. ';
 }
}

// fire initial update based on the first radio
updateParameters(radios[0]);

// helper functions
function updateParameters(elem) {
 fillingsAllowed = elem.getAttribute('data-qty');
 totalPrice.textContent = 'Amount: ' + currency + elem.getAttribute('data-price');
 fillingsPrompt.textContent = 'Select ' + fillingsAllowed + ' fillings';
}
function getSelectedCount() {
 var options = fillings.options, count = 0;
 for (var i=0; i < options.length; i++) {
  if (options[i].selected) count++;
 }
 return count;
}
function getSelectedValues() {
 var options = fillings.options, values = [0];
 for (var i=0; i < options.length; i++) {
  if (options[i].selected) values.push(options[i].value);
 }
 return values;
}
function resetSelect() {
 var options = fillings.options;
 for (var i=0; i < options.length; i++) {
  options[i].selected = false;
  if (currentSelection.indexOf(options[i].value) != -1) {
   options[i].selected = true;
  }
 }
}
fieldset { 
 margin: 1vw; font-family: monospace; 
 display: inline-block; width: 40vw; vertical-align: top;
}
legend { color: #d33; padding: 0px 4px; }
legend::before { content: '[ '; }
legend::after { content: ' ]'; }
fieldset > label { display: block; margin: 4px 0px; }
fieldset input, fieldset span { vertical-align: middle; }
fieldset > select { width: 100%; font-family: monospace; }
input[type=submit] { margin-top: 12px; }
#totalPrice, #result { 
 height: 24px; line-height: 24px; 
 background-color: #dde; padding: 4px;
 font-family: monospace;
}
#result { color: #d33; font-family: monospace; }
<form action="" method="post" id="menuform" name="menuform">
 <fieldset>
  <legend>Select menu option</legend>
  <label>
   <input type="radio" name="selectedmenu" checked 
    value="menu01" data-qty='3' data-price='20' />
   <span>1 -  serves 3 courses ()</span>
  </label>
  <label>
   <input type="radio" name="selectedmenu" 
    value="menu02" data-qty='4' data-price='25' />
   <span>2 - serves 4 courses ()</span>
  </label>
  <label>
   <input type="radio" name="selectedmenu" 
    value="menu03" data-qty='5' data-price='35' />
   <span>3 - serves 5 courses ()</span>
  </label>
  <label>
   <input type="radio" name="selectedmenu" 
    value="menu04" data-qty='6' data-price='75' />
   <span>4 - serves 6 courses ()</span>
  </label>
 </fieldset>
 <fieldset>
  <legend id='fillingsPrompt'>Select fillings</legend>
  <select id="fillings" name="fillings" size="6" multiple>
   <option value="1">Dish 1 ()</option>
   <option value="2">Dish 2 ()</option>
   <option value="3">Dish 3 ()</option>
   <option value="4">Dish 4 ()</option>
   <option value="5">Dish 5 ()</option>
   <option value="6">Dish 6 ()</option>
  </select>
 </fieldset>
 <fieldset>
  <legend>Checkout</legend>
  <div id="totalPrice"></div>
  <input type="submit" value="Submit" />
 </fieldset>
 <fieldset>
  <legend>Messages</legend>
  <p id="result"></p>
 </fieldset>
</form>
<hr>


...how can I change <option> and use <input type="checkbox"> instead for the SECOND STEP?

为了使用复选框而不是 select,不需要进行重大更改。

已更改 mark-up:

<fieldset>
    <legend id='fillingsPrompt'>Select fillings</legend>
    <label>
        <input type='checkbox' name='fillings' value="1" />
        <span>Dish 1 ()</span>
    </label>
    ...

JavaScript 变化:

  1. 为复选框添加 event-handlers 而不是 select,只需要迭代这些:

(就像收音机已经做的一样)

for (var i = fillings.length; i--; ) {
    fillings[i].addEventListener('change', handleFillings);
}
  1. 在所有辅助函数中,删除选项的变量声明:

(因为现在不再需要)

var options = fillings.options
  1. 并且,在所有辅助函数中,

    更改:options.lengthoptions[i].selected

    分别为fillings.lengthfillings[i].checked

就是这样。

Fiddle 2: https://jsfiddle.net/abhitalks/hp88wdfc/

代码段 2:

var menuform = document.getElementById('menuform'), 
 radios = document.getElementsByName('selectedmenu'), 
 fillings = document.getElementsByName('fillings'), 
 fillingsPrompt = document.getElementById('fillingsPrompt'), 
 totalPrice = document.getElementById('totalPrice'), 
 result = document.getElementById('result'), 
 fillingsAllowed = 0, currentSelection = [], currency = '$'
;

// listen to events 
menuform.addEventListener('submit', handleSubmit);
for (var i = fillings.length; i--; ) {
    fillings[i].addEventListener('change', handleFillings);
}
for (var i = radios.length; i--; ) {
    radios[i].addEventListener('change', handleLimit);
}

// event handlers
function handleLimit(e) { updateParameters(e.target); }
function handleFillings(e) {
 var count = getSelectedCount();
 if (count > fillingsAllowed) { resetSelect(); }
 else { currentSelection = getSelectedValues(); }
}
function handleSubmit(e) {
 var count = getSelectedCount();
 e.preventDefault();
 if (count != fillingsAllowed) { 
  result.textContent = 'Must select exactly ' + fillingsAllowed + ' fillings!';
 } else {
  result.textContent = 'Ok. ';
 }
}

// fire initial update based on the first radio
updateParameters(radios[0]);

// helper functions
function updateParameters(elem) {
 fillingsAllowed = elem.getAttribute('data-qty');
 totalPrice.textContent = 'Amount: ' + currency + elem.getAttribute('data-price');
 fillingsPrompt.textContent = 'Select ' + fillingsAllowed + ' fillings';
}
function getSelectedCount() {
 var count = 0;
 for (var i=0; i < fillings.length; i++) {
  if (fillings[i].checked) count++;
 }
 return count;
}
function getSelectedValues() {
 var values = [0];
 for (var i=0; i < fillings.length; i++) {
  if (fillings[i].checked) values.push(fillings[i].value);
 }
 return values;
}
function resetSelect() {
 for (var i=0; i < fillings.length; i++) {
  fillings[i].checked = false;
  if (currentSelection.indexOf(fillings[i].value) != -1) {
   fillings[i].checked = true;
  }
 }
}
fieldset { 
 margin: 1vw; font-family: monospace; 
 display: inline-block; width: 40vw; vertical-align: top;
}
legend { color: #d33; padding: 0px 4px; }
legend::before { content: '[ '; }
legend::after { content: ' ]'; }
fieldset:first-of-type > label { display: block; margin: 4px 0px; }
fieldset:nth-of-type(2) > label { 
 display: inline-block; width: 45%; 
}
fieldset input, fieldset span { vertical-align: middle; }
input[type=submit] { margin-top: 12px; }
#totalPrice, #result { 
 height: 24px; line-height: 24px; 
 background-color: #dde; padding: 4px;
 font-family: monospace;
}
#result { color: #d33; font-family: monospace; }
<form action="" method="post" id="menuform" name="menuform">
 <fieldset>
  <legend>Select menu option</legend>
  <label>
   <input type="radio" name="selectedmenu" checked 
    value="menu01" data-qty='3' data-price='20' />
   <span>1 -  serves 3 courses ()</span>
  </label>
  <label>
   <input type="radio" name="selectedmenu" 
    value="menu02" data-qty='4' data-price='25' />
   <span>2 - serves 4 courses ()</span>
  </label>
  <label>
   <input type="radio" name="selectedmenu" 
    value="menu03" data-qty='5' data-price='35' />
   <span>3 - serves 5 courses ()</span>
  </label>
  <label>
   <input type="radio" name="selectedmenu" 
    value="menu04" data-qty='6' data-price='75' />
   <span>4 - serves 6 courses ()</span>
  </label>
 </fieldset>
 <fieldset>
  <legend id='fillingsPrompt'>Select fillings</legend>
  <label>
   <input type='checkbox' name='fillings' value="1" />
   <span>Dish 1 ()</span>
  </label>
  <label>
   <input type='checkbox' name='fillings' value="2" />
   <span>Dish 2 ()</span>
  </label>
  <label>
   <input type='checkbox' name='fillings' value="3" />
   <span>Dish 3 ()</span>
  </label>
  <label>
   <input type='checkbox' name='fillings' value="4" />
   <span>Dish 4 ()</span>
  </label>
  <label>
   <input type='checkbox' name='fillings' value="5" />
   <span>Dish 5 ()</span>
  </label>
  <label>
   <input type='checkbox' name='fillings' value="6" />
   <span>Dish 6 ()</span>
  </label>
 </fieldset>
 <fieldset>
  <legend>Checkout</legend>
  <div id="totalPrice"></div>
  <input type="submit" value="Submit" />
 </fieldset>
 <fieldset>
  <legend>Messages</legend>
  <p id="result"></p>
 </fieldset>
</form>
<hr>