Javascript 根据选择添加结果(HTML、CSS JAVASCRIPT)

问题描述 投票:0回答:3

我有一个 HTML 页面,有一个数据列表和一个输入类型单选按钮,我想通过按底部的按钮根据数据列表上的选择进行计算(每个选择应附加到一个值,例如 Κρητη 与85 等)以及无线电上的选择(第一个点应将数据列表中的值乘以一,第二个点应乘以二等)。我对英文翻译做了一些修改,使其更容易。为了解释更多,我的意思是:用户从数据列表中选择一个选项,在我的示例中,选项是一些岛屿,用户还选择他希望在岛上停留多少个晚上。每个岛屿都有自己的价格。我想要做的是计算成本(每晚岛屿价格 * 住宿晚数),我的问题是如何保存用户选择以及如何保存变量以便进行计算。我希望我能被更多人理解。

<div id="nhsia">

  <label for="destinationselect">select destination:</label>
  <input list="choices" id="destinations" onmousedown="this.value='';" onchange="setImage(value);" name="destinations" />

  <datalist id="choices">
    <option value="island1">
    <option value="island2">
    <option value="island3">
    <option value="island4">
    <option value="island5">
  </datalist>


</div>

<div id="prices">
  <p id="priceisland1">for island 1 is 85 a night</p>
  <p id="priceisland2">for island 2 is 70 a night</p>
  <p id="priceisland3">for island 3 is 65 a night</p>
  <p id="priceisland4">for island 4 is 55 a night</p>
  <p id="priceisland5">for island 5 is 60 a night</p>
</div>

<div id="nights">
  <label>
    <input type="radio" name="night" value="1">
    ONE NIGHT
  </label>

  <label>
    <input type="radio" name="night" value="2">
    TWO NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="3">
    THREE NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="4">
    FOUR NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="5">
    FIVE NIGHTS
  </label>
</div>


<div id="results">
  <p id="calculate"> CALCULATE VACATION COST!</p>
  <input type="button" id="button" value="calculate!" />
  <p id="results">results: <span></span></p>
</div>

javascript html css
3个回答
0
投票

此问题的一种可能的解决方案如下:

// caching a reference to the <form> element in the page,
// document.querySelector() returns either the first node
// that matches the supplied CSS selector or null:
const form = document.querySelector('form'),
// here we have an Object Literal, which matches the
// islands to their per-night price (as the variable
// name implies):
    costPerNight = {
      'island1': 85,
      'island2': 70,
      'island3': 65,
      'island4': 55,
      'island5': 60,
    };

// we bind the anonymous Arrow function as the event-handler for
// the 'input' event when it's fired on any descendant element
// within the <form>, and pass the Event Object into that
// function:
form.addEventListener('input', (e) => {
  // here we get a reference to the <form> on which the event-
  // handler was bound:
  let form = e.currentTarget,
  // here we get a reference to the element upon which the
  // 'input' event was originall fired:
      affected = e.target,
  // here we use Element.querySelectorAll() to retrive all
  // elements within the <form> that match the supplied
  // CSS selector; Element.querySelectorAll() returns a
  // non-live NodeList of elements, and this is converted
  // to an Array using the Array-literal operator along with
  // the spread syntax:
      checked = [...form.querySelectorAll(':checked')]
      // we then use the anonymous Arrow function of the the
      // Array.prototype.filter() to filter that Array of nodes,
      // passing the current node of the Array of Nodes into
      // the function:
        .filter(
          (el) => {
            // using Element.matches() to obtain a Boolean (true/false)
            // to see if the element matches the CSS selector, so we're
            // checking if the current node is an <option> element, and
            // that the element is the defaultSelected option (this
            // returns a Boolean, true if it is, false if not), and checking
            // if it is also disabled (again, returning a Boolean).
            // The reason for this is that by default the first <option>
            // of a <select> element will be selected by default, and will
            // match the ':checked' CSS selector, but I want to ensure that
            // the user is forced to choose their own preference rather
            // than accepting the default:
            if (el.matches('option') && el.defaultSelected && el.disabled) {
              // if all of the above evaluates to true, then we return
              // false and remove the <option> from the Array of nodes:
              return false;
            }
            // otherwise we return true:
            return true
          }),
      // retrieving the <fieldset> elements:
      fieldsets = form.querySelectorAll('fieldset'),
      // a switch, that assesses whether the number of
      // :checked items is equal to the number of
      // <fieldset> elements:
      haveBoth = checked.length === fieldsets.length,
      // caching references to elements:
      result = document.querySelector('#result'),
      cost = document.querySelector('#cost');

  // here we assess the result of haveBoth, and invert it;
  // so if we have the same number of :checked elements as
  // the number of <fieldset> elements haveBoth would be
  // true, and here we invert that to false (and vice versa).
  // We want to enter this branch of the if only when hasBoth
  // is false, so we invert the false to true, if hasBoth
  // is itself true we don't wish to enter this branch so
  // we invert the true to false in order to skip it:
  if (!haveBoth) {
    // here we hide the result element when hasBoth is false,
    // and inverted to true:
    result.hidden = true;
  } else {
    // here we use Array.prototype.map() to iterate over the
    // Array of Nodes to return a different Array based on
    // that existing Array:
    checked.map(
      // we again use the anonymous Arrow function of the
      // Array.prototype.map() method to pass the current
      // node of the Array into the function:
      (el) => {
        // we retrieve the closest ancestor-element of the
        // current Node which has a 'name' attribute; in
        // the case of <input> elements that is the <input>
        // element itself, and in the case of the <option>
        // elements it is the parent <select> element:
        let hasName = el.closest('[name]'),
          // we use destructuring assignment to assign the
          // named property-values of the hasName Node to
          // the supplied variables:
          {
            name,
            value
          } = hasName;
          
        // here we use the HTMLOrForeignElement.datalist API
        // to create a data-* attribute on the cost element,
        // setting its property-value to that held in the
        // current value variable:
        cost.dataset[name] = value;
        // we then return an Object literal, with two
        // properties, name and value:
        return {
          'name': hasName.name,
          'value': hasName.value
        }
      // we use Array.prototype.forEach() to iterate over the
      // Array of Objects returned by Array.prototype.map(),
      // passing the current Object of that Array into the
      // function as the 'obj' variable:
      }).forEach(
        // here we use a template literal to retrieve supply the
        // name of the current Object to document.querySelector()
        // to retrieve the node that will hold the current Object's
        // value property-value:
        (obj) => document.querySelector(`#${obj.name}`).textContent = obj.value
      );
      
    // here we use another template-literal to compose the key with
    // which we can use bracket-notation to access the per-night cost
    // of the selected island, retrieving that chosen island's number
    // from the created data-* attribute on the cost Node (earlier):
    let perNight = costPerNight[`island${cost.dataset.island}`],
    // here we retrieve the number of nights the user has chosen to stay,
    // again using the HTMLOrForeignElement.dataset API, and then
    // use parseInt() (with a radix of 10) to convert that string into
    // a base-10 number:
        nights = parseInt(cost.dataset.nights, 10);
        
    // we update the text-content of the cost element to be equal to
    // the result of multiplying the perNight and nights variables:
    cost.textContent = perNight * nights;
    // we set the hidden property of the result element to false (so
    // that it becomes visible):
    result.hidden = false;
  }
});
*,
::before,
::after {
  box-sizing: border-box;
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
  padding: 0;
}

form {
  width: 80vw;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5em;
}

fieldset {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}

fieldset + div {
  flex: 2 0 auto;
}

h2::after {
  content: ':';
}

select,
label {
  width: 80%;
  margin-inline: auto;
}

label span {
  margin-inline-start: 0.5em;
}

input:checked + span {
  font-weight: bold;
}

#result {
  grid-row: auto;
  grid-column: 1 / -1;
}

#result span {
  font-weight: bold;
}

#island:not(:empty)::before {
  content: 'Island ';
}

#nights:not(:empty)::after {
  content: ' nights';
}

#cost:not(:empty)::before {
  content: attr(data-currency);
}
<!-- Because we're taking inputs from a user we're going to use a <form>, and
     associated form-elements, to gather that information: -->
<form action="#" method="post">
  <!-- grouping related information together using <fieldset> elements: -->
  <fieldset>
    <!-- using a <legend> element to provide a clear heading, though
         we also use a nested <h2> element to prompt the user to
         answer (this could be a <label>, but I wanted to have the <h2>
         elements to behave similarly in both <fieldset> elements: -->
    <legend>Destination</legend>
    <h2>Which Island</h2>
    <select name="island">
      <!-- here we explicitly specify that the first <option>,
           which contains an instruction to the user, is both
           selected (so it's defaultSelected property will be
           true, and that it is disabled so that it cannot be
           selected by the user: -->
      <option disabled selected>Please select:</option>
      <option value="1">Island 1 for 85 per night</option>
      <option value="2">Island 2 for 70 per night</option>
      <option value="3">Island 3 for 65 per night</option>
      <option value="4">Island 4 for 55 per night</option>
      <option value="5">Island 5 for 60 per night</option>
    </select>
  </fieldset>
  <fieldset>
    <legend>Duration</legend>
    <h2>How long are you staying</h2>
    <label><input type="radio" name="nights" value="1"><span>1 night</span></label>
    <label><input type="radio" name="nights" value="2"><span>2 nights</span></label>
    <label><input type="radio" name="nights" value="3"><span>3 nights</span></label>
    <label><input type="radio" name="nights" value="4"><span>4 nights</span></label>
    <label><input type="radio" name="nights" value="5"><span>5 nights</span></label>
  </fieldset>
  <!-- we use the "hidden" attribute to hide the element on page-load, this element
       will show the user their choices once those choices have been made: -->
  <div id="result" hidden>
    You have selected <span id="island"></span> for <span id="nights"></span>, at a cost of <span id="cost" data-currency="€"></span>.
  </div>
</form>

JS Fiddle 演示.

参考资料:


0
投票

由于您使用的是 javascipt,因此最好将所有列表存储在一个位置并生成 html。

const data = [
  {
    name: "island 1",
    price: 85
  },
  {
    name: "island 2",
    price: 70
  },
  {
    name: "island 3",
    price: 65
  },
  {
    name: "island 4",
    price: 55
  },
  {
    name: "island 5",
    price: 60
  }
]

const destinationsCtl = document.getElementById("destinations"),
      calculateCtl = document.getElementById("calculate"),
      choicesCtl = document.getElementById("choices"),
      pricesCtl = document.getElementById("prices"),
      priceTempl = prices.querySelector(".price"),
      nightsCtl = document.getElementById("nights"),
      nightsList = nightsCtl.querySelectorAll('input[type="radio"][name="night"]'),
      resultsCtl = document.getElementById("results"),
      regId = new RegExp("\\${(" + Object.keys(data[0]).join("|") + ")}", "g");

prices.removeChild(priceTempl);

var selectedDest = null,
    selectedNights = 0;

for(let i = 0; i < data.length; i++)
{
  const option = document.createElement("option");
  option.value = data[i].name;
  option._dataId = i;
  option.label = data[i].price + " a night";
  choicesCtl.appendChild(option);
  
  price = priceTempl.cloneNode(true);
  price.innerHTML = price.innerHTML.replace(regId, (a, key) => data[i][key]);
  pricesCtl.appendChild(price);
}

for(let i = 0; i < nightsList.length; i++)
{
  nightsList[i].addEventListener("input", (e) => 
  {
    selectedNights = ~~e.target.value;
    setImage();
  });
}

function setImage(dest)
{
  if (dest === undefined)
    dest = destinationsCtl;

  selectedDest = null;
  for(let i = 0; i < dest.list.options.length; i++)
  {
    const selected = dest.list.options[i].value === dest.value;
    if (selected)
    {
      selectedDest = dest.list.options[i]._dataId;
    }
    pricesCtl.children[i].classList.toggle("selected", selected);
  }
  calculate();
}

function calculate()
{
  if (selectedDest === null || !selectedNights)
    return;

  const price = data[selectedDest].price,
        destination = data[selectedDest].name,
        nights = selectedNights,
        totalPrice =  price * nights;
         
  resultsCtl.textContent = `${totalPrice} for ${nights} nights at ${destination}`;
}

setImage();
.price.selected
{
  font-weight: bold;
}
<div id="nhsia">

  <label for="destinationselect">select destination:</label>
  <input list="choices" id="destinations" oninput="setImage(this);" name="destinations" />

  <datalist id="choices">
  </datalist>


</div>

<div id="prices">
  <p class="price">for ${name} is ${price} a night</p>
</div>

<div id="nights">
  <label>
    <input type="radio" name="night" value="1">
    ONE NIGHT
  </label>

  <label>
    <input type="radio" name="night" value="2">
    TWO NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="3">
    THREE NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="4">
    FOUR NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="5">
    FIVE NIGHTS
  </label>
</div>


<div class="results">
  <p>results: <span id="results"></span></p>
</div>


0
投票

<div id="nhsia">

  <label for="destinationselect">select destination:</label>
  <input list="choices" id="destinations" onmousedown="this.value='';" onchange="setImage(value);" name="destinations" />

  <datalist id="choices">
    <option value="island1">
    <option value="island2">
    <option value="island3">
    <option value="island4">
    <option value="island5">
  </datalist>


</div>

<div id="prices">
  <p id="priceisland1">for island 1 is 85 a night</p>
  <p id="priceisland2">for island 2 is 70 a night</p>
  <p id="priceisland3">for island 3 is 65 a night</p>
  <p id="priceisland4">for island 4 is 55 a night</p>
  <p id="priceisland5">for island 5 is 60 a night</p>
</div>

<div id="nights">
  <label>
    <input type="radio" name="night" value="1">
    ONE NIGHT
  </label>

  <label>
    <input type="radio" name="night" value="2">
    TWO NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="3">
    THREE NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="4">
    FOUR NIGHTS
  </label>

  <label>
    <input type="radio" name="night" value="5">
    FIVE NIGHTS
  </label>
</div>


<div id="results">
  <p id="calculate"> CALCULATE VACATION COST!</p>
  <input type="button" id="button" value="calculate!" />
  <p id="results">results: <span></span></p>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.