3
\$\begingroup\$

I've written a WordPress plugin that displays a price quote based on what options are chosen in the settings and then what the user chooses with HTML <select> dropdown menus. Here's the HTML (with a little PHP as well). Note that the function at the top is just so I can use WordPress's get_option and use those values in my JS:

 function dataToJS () {
  global $get_option_array;
  wp_enqueue_script( 'added_jquery', plugin_dir_url( __FILE__ ) . 'js/added_jquery.js', array( 'jquery' ), '1.0', true );
  wp_localize_script( 'added_jquery', 'script_vars', array (
      'bedrooms_num' => __($get_option_array['bedroom']),
      'family_rooms_num' => __($get_option_array['family_room']),
      'living_rooms_num' => __($get_option_array['living_room']),
      'offices_num' => __($get_option_array['office'])
    ) );
}

add_filter( 'wp_enqueue_scripts', 'dataToJS');

<form name="quote" id="price-quote" action="/#" method="post">
        if (!empty($get_option_array)) {
          if ($get_option_array['bedroom'] != 0) {
            ?><label class="control-label" for="bedrooms">Bedrooms</label><br>
            <select class="form-control-1" id="bedrooms" name ="bedrooms_selection">
              <option value="0">0</option>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
              <option value="6">6</option>
              <option value="7">7</option>
            </select>
            <?php
          }

          if ($get_option_array['family_room'] != 0) {
            ?><br><label class="control-label" for="family_rooms">Family Rooms</label><br>
            <select class="form-control-1" id="family_rooms" name="family_room_selection">
              <option value="0">0</option>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
            </select>
            <?php
          }

          if ($get_option_array['living_room'] != 0) {
            ?><br><label class="control-label" for="living_rooms">Living Rooms</label><br>
            <select class="form-control-1" id="living_rooms" name="living_room_selection">
              <option value="0">0</option>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
            </select>
            <?php
          }

 <div class="total-price">
   <h2>Total Price: <span id="output1" name="total_price"></span></h2>
 </div>
</form>

It works, and the JavaScript below does display the right price, but I'm realizing I need a better way to display what price is selected. I want to be able to display options based on if the admin has added a value for each service. For example, if the admin wants to show 'bedrooms' and 'family rooms' but not 'living rooms', then only the first two would show. I've set this up, but here's my JavaScript that is quickly becoming a mess as you can see:

  var total;
  var totalPrice;
  var bedrooms;
  var familyRooms;
  var livingRooms;


  //price input from admin screen
  var bedroom_price = script_vars.bedrooms_num;
  var family_rooms_price = script_vars.family_rooms_num;
  var living_rooms_price = script_vars.living_rooms_num;

  if (bedroom_price != 0 && family_rooms_price == 0){

    $("#bedrooms").change(function() {

      bedrooms = $("#bedrooms").val();
      total = (bedrooms * bedroom_price);
      totalPrice = "$" + total;

      $("#output1").text(totalPrice).fadeIn();
    });
}

  if (bedroom_price != 0 && family_rooms_price != 0){

    $("#bedrooms, #family_rooms").change(function() {

      bedrooms = $("#bedrooms").val();
      familyRooms = $("#family_rooms").val();
      total = (bedrooms * bedroom_price);
      total += (familyRooms * family_rooms_price);
      totalPrice = "$" + total;

      $("#output1").text(totalPrice).fadeIn();
    });
}

if (bedroom_price == 0 && family_rooms_price != 0){

  $("#family_rooms").change(function() {

    familyRooms = $("#family_rooms").val();
    total = (familyRooms * family_rooms_price);
    totalPrice = "$" + total;

    $("#output1").text(totalPrice).fadeIn();
  });
}

I have about 20 options total that the user will be able to select, so these if statements are going to be ridiculous once there are more than 3 select lists.

I'm a designer transitioning to development so this is my first real project. So if you see some other major issues, I'd really appreciate the feedback.

\$\endgroup\$
2
\$\begingroup\$

Making the Javascript code more versatile

You can query the DOM for all select list elements (i.e. <select>) to handle the change event on any of them.

$(function() { //wait for DOM to be Ready
  //find all <select> tags in the document
  var selects = $('select');
  //when any select list changes, run the callback function
  selects.change(function(changeEvent) { });

Then in the callback to .change(), accept an argument for the change event - e.g. changeEvent- and check the target property

selects.change(function(changeEvent) { });
    var changedSelectList = changeEvent.target;
    //only update the total if there is a price
    if (Object.keys(prices).includes(changedSelectList.id) && prices[changedSelectList.id]) {

Notice that conditional checks that the id attribute of the select list is included in the keys (using Object.keys() and Array.includes()) of an object called prices. With this this approach, when adding the price variables (with wp_localize_script( 'added_jquery', 'script_vars',) try adding the prices to an object instead of individual variables, like below:

prices = {
 'bedrooms': 4,
 'family_rooms': 6,
 'living_rooms': 7
};

I am not really experienced with wp_localize_script() but I presume that would look like:

 wp_localize_script( 'added_jquery', 'script_vars', array (
  'prices' => array(
      'bedrooms' => __($get_option_array['bedroom']),
      'family_rooms' => __($get_option_array['family_room']),
      'living_rooms' => __($get_option_array['living_room']),
      'offices' => __($get_option_array['office'])
  )
) );

Then, (Back in the Javascript change callback) in order to calculate the total price, iterate over the select lists and look for a selected option under each select list. The code below uses Array.reduce() to get the sum of the prices by iterating over the select lists. It also uses the spread syntax (i.e. [...selects]) to get an array from the jQuery collection.

var totalPrice = [...selects].reduce(function(sum, select) {
    if ($(select).val()) {
      sum += $(select).val() * prices[select.id];
    }
    return sum; //return cumulative total
}, 0); //start the total at 0
$("#output1").text(totalPrice); //update the output

Notice that in order to look up the price for the associated select list, use the id attribute (e.g. family_rooms) to get the price value from prices.

Rendering options

Additionally, when rendering the HTML form with Wordpress, PHP's foreach and range() could be combined to generate the options, like in the example below:

<select class="form-control-1" id="bedrooms" name ="bedrooms_selection">
    <?php
    foreach(range(0,7) as $i) {
        echo '<option value="'.$i.'">'.$i.'</option>';
    }
    ?>
</select>

See a full demo of the three select lists in this playground example.

Demo (for Javascript changes)

Try out the sample below. The SO snippets only support HTML & Javascript so obviously the wordpress/ PHP code has been removed.

//define prices as simple JS object, where keys will match the id attributes of the select tags 
prices = {
  'bedrooms': 4,
  'family_rooms': 6,
  'living_rooms': 7
};
$(function() { //wait for DOM to be Ready
  //find all <select> tags in the document
  var selects = $('select');
  //when any select list changes, run the callback function
  selects.change(function(changeEvent) {
    var changedSelectList = changeEvent.target;
    //only update the total if there is a price
    if (Object.keys(prices).includes(changedSelectList.id) && prices[changedSelectList.id]) {
      //iterate over select lists and sum the prices
      var totalPrice = [...selects].reduce(function(sum, select) {
        //if there is a selected value for the select list
        if ($(select).val()) {
          //add the product of the selected value and the price
          sum += $(select).val() * prices[select.id];
        }
        //return the sum for the next iteration of the loop
        return sum;
      }, 0); //start the total at 0
      //update the output
      $("#output1").text(totalPrice)
    }

  });
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<form name="quote" id="price-quote" action="/#" method="post">
  <label class="control-label" for="bedrooms">Bedrooms</label><br>
  <select class="form-control-1" id="bedrooms" name="bedrooms_selection">
              <option value="0">0</option>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
              <option value="6">6</option>
              <option value="7">7</option>
            </select>

  <br><label class="control-label" for="family_rooms">Family Rooms</label><br>
  <select class="form-control-1" id="family_rooms" name="family_room_selection">
              <option value="0">0</option>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
            </select>
  <br><label class="control-label" for="living_rooms">Living Rooms</label><br>
  <select class="form-control-1" id="living_rooms" name="living_room_selection">
              <option value="0">0</option>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
            </select>
  <div class="total-price">
    <h2>Total Price: <span id="output1" name="total_price"></span></h2>
  </div>
</form>

\$\endgroup\$
  • \$\begingroup\$ This was above and beyond, and the explanation was really helpful. Thank you. No worries on the PHP, I got it all to work seamlessly \$\endgroup\$ – Coltvant Aug 31 '17 at 17:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.