Use selected element when selector allow for multiple options

Viewed 97

Sorry if my title is difficult to understand.

I've multiple fields named date1, date2... On Change of those fields, I want a full script to run. Today, I have simply copied the code for each dateN I have.

I would like to have a cleaner code and use the next JQuery selector:

$("input[id^=date]")

The problem I face is that I don't know how to reuse the selected element into the subsequent code. I've tried with $(this) but it doesn't work, as it seems to be undefined.

Edit: as requested I show an example with $(this). The variable day stays empty. It works if I use the same selector as for month and year. The full example doesn't use $(this) yet but it's to show that my $(this) doesn't work.

$("#date1").on("change", () => {
      // Check validity of the date
      const regexDate = /\d{2}\/\d{2}\/\d{4}/gm;
      if (regexDate.test($("#date1").val()) == false) {
        // Date format not valid, we quit the function
        $("#info1").removeAttr("title");
        $("#info1").hide();
        return;
      }

      //Slice date
      var day = $(this).val().substring(0, 2);
      var month = $("#date1").val().substring(3, 5);
      var year = $("#date1").val().substring(6, 10);

      $.get("http://192.168.1.6:3000/getDateHistory/" + day + "/" + month + "/" + year, (data) => {
        data = JSON.parse(data);
        // Only display if not empty
        if (data.length > 0) {
          historyOfSameDate = "Other records exist at the same date: ";
          for (var i = 0; i < data.length; i++) {
            historyOfSameDate += data[i].desc + " - " + data[i].amount;
            if (i < data.length - 1) {
              historyOfSameDate += " | "
            };
          }
          $("#info1").attr({
            "title": historyOfSameDate
          });
          $("#info1").show();
        } else {
          $("#info1").removeAttr("title");
          $("#info1").hide();

        }
      });
<tr>
  <td><input class="form-control datepicker" id="date1" placeholder="Enter date"></td>
  <td style="vertical-align:middle;"><i id="info1" class="bi bi-info-square-fill"></i></td>
  <td>
    <select class="form-control" id="type1" placeholder="Enter type">
      <option value=""></option>
    </select>
  </td>
  <!-- Options dynamically created -->
  <td><input class="form-control" id="desc1" placeholder="Enter description"></td>
  <td><input class="form-control" id="amount1" placeholder="Enter amount"></td>
  <td>
    <select class="form-control" id="payer1" placeholder="Enter type">
      <option value=""></option>
    </select>
  </td>
  <!-- Options dynamically created -->
  <td>
    <select class="form-control" id="paymentsource1" placeholder="Enter type">
      <option value=""></option>
    </select>
  </td>
  <!-- Options dynamically created -->
  <td style="vertical-align:middle; color:orange;"><i id="warning1" class="bi bi-exclamation-triangle-fill" title="This record will be ignored because it either doesn't contain enough information or contains error(s)."></i></td>
</tr>
<tr>
  <td><input class="form-control datepicker" id="date2" placeholder="Enter date"></td>
  <td style="vertical-align:middle;"><i id="info2" class="bi bi-info-square-fill"></i></td>
  <td>
    <select class="form-control" id="type2" placeholder="Enter type">
      <option value=""></option>
    </select>
  </td>
  <!-- Options dynamically created -->
  <td><input class="form-control" id="desc2" placeholder="Enter description"></td>
  <td><input class="form-control" id="amount2" placeholder="Enter amount"></td>
  <td>
    <select class="form-control" id="payer2" placeholder="Enter type">
      <option value=""></option>
    </select>
  </td>
  <!-- Options dynamically created -->
  <td>
    <select class="form-control" id="paymentsource2" placeholder="Enter type">
      <option value=""></option>
    </select>
  </td>
  <!-- Options dynamically created -->
  <td>

Thank you

2 Answers

Discrepancy

"I've tried with $(this) but it doesn't work, as it seems to be undefined."

Arrow functions do not recognize this you need to use either a named function or anonymous function. Also, there's a second parameter that accepts a selector which designates this:

// Named Function
$(selector).on(event, ".thisClassWillBeTHIS", functionName);
function functionName(e) {...

// Anonoymous Function 
$(selector).on(event, "input", function(e) {...
// "input" means any `<input>` in selector

In the following example, next() isn't used as you probably expected to be used, in fact, .next() is used just for demonstration purposes. .next() if applied to the <input> (this) wouldn't jump over to the next cell (<td>) -- you'll need to traverse out of the cell of this then use .next() and then use .find() and so on.

Instead of traversing each cell of a row, you should take advantage of all of those #ids (although I discourage the use of #id under most circumstances). Each #id has everything in common but the number so you can get the #id of this and strip the number and store it in a variable to concatenate to other elements within that row:

const set = this.id.match(/\d{1,2}/g).toString();
$('#info'+set).attr('title')

<table> is wrapped in a <form> and the event handler is bound to <form>. On an ancestor <form> element and using a form event ("change") gives you more control by using event delegation and HTMLFormElement interface which is a plain JavaScript interface that has terse syntax and special features (optional, it'll be there should you need it):

$('form').on('change', ...

<output> is placed after each #info* to display messages since I have no clue why "title" attribute is being used (that's where .next() is used).

There is very little left of the OP code since the majority has dependencies that were not included (don't need it either). Most of he changes are for demonstration purposes and the only things you should keep are:

$('form').on('change', '.datepicker', function(e) {
  const set = this.id.match(/\d{1,2}/g).toString();
  /* Replace `$('info1') with... */
  $("#info" + set)

And wrap the <table> in a <form> if you haven't already. BTW, if you add type="date" to .datepicker it will magically turn into a real datepicker and that date check wouldn't be needed.

Example

const display = D => {
  const options = {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  };
  return new Intl.DateTimeFormat('en-US', options).format(D);
};

$("form").on("change", '.datepicker', function(e) {

  const set = this.id.match(/\d{1,2}/g).toString();

  const regexDate = /\d{2}\/\d{2}\/\d{4}/g;

  if ($(this).val() == '') {
    $("#info" + set).next().html('');
    return;
  }

  if (regexDate.test($(this).val()) == false) {
    $("#info" + set).next().html(`<b>Invalid Date</b>`);
    return;
  }
  const date = new Date($(this).val());

  $("#info" + set).next().html(`<time>${display(date)}</time>`);
});
<form>
  <table>
    <tr>
      <td><input class="form-control datepicker" id="date1" placeholder="MM/DD/YYYY"></td>
      <td style="vertical-align:middle;"><i id="info1" class="bi bi-info-square-fill"></i>
        <output></output>
      </td>
      <td>
        <select class="form-control" id="type1" placeholder="Enter type">
          <option value=""></option>
        </select>
      </td>
      <td><input class="form-control" id="desc1" placeholder="Enter description"></td>
      <td><input class="form-control" id="amount1" placeholder="Enter amount"></td>
      <td>
        <select class="form-control" id="payer1" placeholder="Enter type">
          <option value=""></option>
        </select>
      </td>
      <td>
        <select class="form-control" id="paymentsource1" placeholder="Enter type">
          <option value=""></option>
        </select>
      </td>
      <td style="vertical-align:middle; color:orange;"><i id="warning1"></i></td>
    </tr>
    <tr>
      <td><input class="form-control datepicker" id="date2" placeholder="MM/DD/YYYY"></td>
      <td style="vertical-align:middle;"><i id="info2" class="bi bi-info-square-fill"></i>
        <output></output>
      </td>
      <td>
        <select class="form-control" id="type2" placeholder="Enter type">
          <option value=""></option>
        </select>
      </td>
      <td><input class="form-control" id="desc2" placeholder="Enter description"></td>
      <td><input class="form-control" id="amount2" placeholder="Enter amount"></td>
      <td>
        <select class="form-control" id="payer2" placeholder="Enter type">
          <option value=""></option>
        </select>
      </td>
      <td>
        <select class="form-control" id="paymentsource2" placeholder="Enter type">
          <option value=""></option>
        </select>
      </td>
    </tr>
  </table>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Use it like this:

$('input[type=date]').click(function() {
    var fields = $(this);
    // do something with it
    return false;
});
Related