'How can I have multiple functions in onkeyup?

I am writing code to be able to search multiple fields within cards that pull customer data from endpoints. The first function that searches customerID works fine, but I would also like to be able to search the other fields. But I am having trouble getting it recognize multiple fields.

How can I register multiple functions inside an onkeyup field and if I am selecting the forename field in querySelector properly?

My HTML code:

<div class="container pt-3 pb-3">
    <div class="form-outline" id="customer-search-bar">
        <input type="search" id="filter" class="form-control" placeholder="Enter customer ID" aria-label="Search" onkeyup="searchID() && searchForename();" >
    </div>
    <div class="card-lists" id="card-lists">
       <div class="row">
          {% for data in raw.CustomerDataEntries %}
          <div class="card col-sm-12 mb-3" id="card-perim">
             <div class="card-body" id="card-body">
                <h5 class="card-title">Customer ID: {{ data.ExternalId }}</h5>
                <h5 class="card-title" name="forename">Forename: {{ data.Fields.forename }}</h5>
                <h5 class="card-title">Surname: {{ data.Fields.surname }}</h5>
                <h5 class="card-title">Postcode: {{ data.Fields.post_code }}</h5>
                <h5 class="card-title">Matches: {{ data.Matches }}</h5>
                <a href="#" class="tableButton">Details</a>
                <a href="#" class="tableButton">Edit</a>
             </div>
          </div>
          {% endfor %}
       </div>
    </div>
</div>

My JavaScript code:

function searchID() {
    const input = document.getElementById('filter').value.toUpperCase();

    const cardContainer = document.getElementById('card-lists');
    console.log(cardContainer);

    const cards = cardContainer.getElementsByClassName('card-body');
    console.log(cards);

    for (let i = 0; 0 < cards.length; i++){
        let title = cards[i].querySelector(".card-body h5.card-title");
        if(title.innerText.toUpperCase().indexOf(input) > -1){
            cards[i].style.display = "";
        } else {
            cards[i].style.display = "none";
        }
    }
}

function searchForename() {
    const input = document.getElementById('filter').value.toUpperCase();

    const cardContainer = document.getElementById('card-lists');
    console.log(cardContainer);

    const cards = cardContainer.getElementsByClassName('card-body');
    console.log(cards);

    for (let i = 0; 0 < cards.length; i++){
        let title = cards[i].querySelector(".card-body h5[name='forename']");
        if(title.innerText.toUpperCase().indexOf(input) > -1){
            cards[i].style.display = "";
        } else {
            cards[i].style.display = "none";
        }
    }
}


Solution 1:[1]

The value within the onkeyup attribute follows JS syntax, so you should separate the statements with a semi-colon, ;.

<input type="search" id="filter" onkeyup="searchID(); searchForename();" class="form-control" placeholder="Enter customer ID" aria-label="Search" />

However using inline event handlers is not good practice. Modern best practice is to unobtrusively bind your event handlers. In native JS you can achieve this by using addEventListener(), like this:

let filter = document.querySelector('#filter');
filter.addEventListener('keyup', () => {
  searchID();
  searchForename();
});

function searchID() {
  console.log('searchID');
}

function searchForename() {
  console.log('searchForename');
}
<input type="search" id="filter" class="form-control" placeholder="Enter customer ID" aria-label="Search" />

The second function searchForename() still doesn't work for me though

The main reason it doesn't work is partly because it's a duplication of the work being done in searchId(), so anything displayed by searchId() will be hidden in searchForname().

In addition, because you set display state for every .card-item in the card. This means that if the last item does not contain the searched text the entire card is hidden - even if a previous card did contain the text. To fix this hide the card to start with, then display it only if a match is found.

Here's a working example with the search logic corrected - note that I commented out searchForename() in the keyup handler as it works on a subset of the same elements as searchID, so its use there is redundant.

let filter = document.querySelector('#filter');
filter.addEventListener('keyup', () => {
  searchID();
  //searchForename();
});

function searchID() {
  const input = filter.value.toUpperCase();
  const cardContainer = document.querySelector('#card-lists');
  const cards = cardContainer.querySelectorAll('.card-body');

  cards.forEach(card => {
    card.style.display = 'none';
    card.querySelectorAll(".card-body h5.card-title").forEach(title => {
      if (title.innerText.toUpperCase().indexOf(input) > -1) {
        card.style.display = "block";
      }
    });
  });
}

function searchForename() {
  const input = filter.value.toUpperCase();
  const cardContainer = document.querySelector('#card-lists');
  const cards = cardContainer.querySelectorAll('.card-body');

  cards.forEach(card => {
    card.style.display = 'none';
    card.querySelectorAll(".card-body h5.card-title.forename").forEach(title => {
      if (title.innerText.toUpperCase().indexOf(input) > -1) {
        card.style.display = "block";
      } 
    });
  });
}
<div class="container pt-3 pb-3">
  <div class="form-outline" id="customer-search-bar">
    <input type="search" id="filter" class="form-control" placeholder="Enter customer ID" aria-label="Search" />
  </div>
  <div class="card-lists" id="card-lists">
    <div class="row">
      <div class="card col-sm-12 mb-3">
        <div class="card-body">
          <h5 class="card-title">Customer ID: 123</h5>
          <h5 class="card-title forename">Forename: Lorem</h5>
          <h5 class="card-title">Surname: Ipsum</h5>
          <h5 class="card-title">Postcode: 90210</h5>
          <h5 class="card-title">Matches: Foobar</h5>
          <a href="#" class="tableButton">Details</a>
          <a href="#" class="tableButton">Edit</a>
        </div>
      </div>
      <div class="card col-sm-12 mb-3">
        <div class="card-body">
          <h5 class="card-title">Customer ID: 123</h5>
          <h5 class="card-title forename">Forename: Dolor</h5>
          <h5 class="card-title">Surname: Sit</h5>
          <h5 class="card-title">Postcode: 92893</h5>
          <h5 class="card-title">Matches: Fizzbuzz</h5>
          <a href="#" class="tableButton">Details</a>
          <a href="#" class="tableButton">Edit</a>
        </div>
      </div>
    </div>
  </div>
</div>
My JS code:

One last thing - note that I removed the id attributes in the content you create in the loop. This is because repeated id are invalid, they must contain unique values within the DOM.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1