skip to Main Content

I would like to have live search box acting like google , I have implement jquery ajax to backend for getting results , but I want to enable when user press key down i would like to add a class to the ajax return suggestion just like google, when user press up suggestion hightlight will go up a place when user press enter it will go to the href. below are my ajax return html codes:

<ul id="suggestion" style="list-style-type: none;">
<li><a class="suggestion-link" href="http://eled.test/post/sunt-iure-nihil-deleniti-rerum"> Sunt Iure Nihil Deleniti Rerum.</a> 
</li>

<li><a class="suggestion-link" href="http://eled.test/post/porro-et-numquam-nihil-nesciunt-nesciunt"> Porro Et Numquam Nihil Nesciunt Nesciunt.</a> 
</li>

<li><a class="suggestion-link" href="http://eled.test/post/my-new-post-yeah"> My New Post Yeah !!</a> </li>

<li><a class="suggestion-link" href="http://eled.test/post/rerum-voluptatem-fuga-excepturi-provident-et-distinctio"> Rerum Voluptatem Fuga Excepturi Provident Et...</a> 
</li>

</ul>

below is my search form

<form method="GET" action="{{route('home.search')}}" class="search-form" id="search-form" >
              <div class="form-group">

                <input type="text" name="keyword" id="search-input" placeholder="What are you looking for?" autocomplete="off">

                <button type="submit" class="submit"><i class="fas fa-search"></i></button>
              </div>


              <ul id="suggestion" style="display: none;list-style-type: none; "></ul>
            </form>

below is my ajax

$(document).ready(function() {
  //sidebar search function ajax
   $("#search-input").keyup(function() {
       var keyword = $('#search-input').val();
       var url = "{{route('home.ajax_search')}}";
       if (keyword == "") {
           $("#suggestion").html("");
           $('#suggestion').hide();
       }else {
           $.ajax({
               type: "GET",
               url: url ,
               data: {
                   keyword: keyword
               },
               success: function(data) {
                   $("#suggestion").html(data).show();
                   //console.log(data);
                       $('#search-input').on("keydown", function (e) {

                          var listItems = $('.suggestion-link');

                          switch(e.which) {

                              case 38:
                              console.log('up');
                              break;

                              case 40:
                              listItems.addClass('selected');
                              console.log('down');
                              break;
                              default: return;
                          }        

                      });
               }
           });
           
       }

   });

});

inside the ajax i can get console log working when try key down and up. 38 / 40
but I cant add the selected class the li element I follow this Add keydown (keyboard arrow up/down) support for AJAX Live Search PHP
the problem is I could not apply class active to the return element so user wont know the key down/up is working thanks

2

Answers


  1. First of all, when you render results give an ID to each li element. An ID could be the index of the loop. Just make sure IDs are consecutive. Then you can use something like:

    let highlighted = 0;
    
    $('#search-input').on('keydown', function(e) {
       
       //check if pressed key is down or up
       // 40 is a code for down arrow, 38 for up
    
       if([38, 40].indexOf(e.which) < 0) 
          return;
    
        //remove highlighted class from all list elements here
    
        $('#suggestion').find('li.highligted').removeClass('highligted');    
    
        //handle down and up keypress here
        
        hightlighted = e.which == 40 ? highlighted + 1 : highligted - 1;        
        
        $('#suggestion-'+highlighted).addClass('highligted');
    
    });
    
    Login or Signup to reply.
  2. You should not bind events inside success function as for each request it will add a new listener while your old listener is still attached, which can be problematic.

    From your problem statement what I understood was that you want to highlight result URL using up and down arrow key. Here is a short working example which might help you.

    const field = document.getElementById('search');
    
    let focusIndex = -1;
    
    // When field is focussed, you want to enable up and down navigation
    
    field.onfocus = function () {
      this.classList.add('allow-arrow-nav');
    }
    
    field.onblur = function () {
      this.classList.remove('allow-arrow-nav');
    }
    
    // We will use event delegation to detect if "allow-arrow-nav" class is present
    
    document.body.addEventListener('keydown', (e) => {
      if (e.target.classList.contains('allow-arrow-nav')) {
        // Key codes that we need to target are 38 (up) and 40 (down)
        const allLis = document.querySelectorAll('#searchResults li');
        const totalResults = allLis.length;
        if (e.keyCode === 38 && focusIndex > 0) {
          focusIndex--;
        }
        if (e.keyCode === 40 && focusIndex < totalResults - 1) {
          focusIndex++;
        }
        [...allLis].forEach(liEl => liEl.classList.remove('is-focussed'));
        const selectedLi = document.querySelector(`li[focus-index="${focusIndex}"]`);
        if (selectedLi) {
          selectedLi.classList.add('is-focussed');
          const selectedInnerText = selectedLi.textContent;
          e.target.value = selectedInnerText;
          if (e.keyCode === 13) {
            window.open(selectedLi.querySelector('a').getAttribute('href'), '_blank');
          }
        }
      }
    });
    
    // Since we are delegating the events, you can make the list section dynamic and us a separate function to render it. You need to make sure that all relevant attributes, ids and classes are present to make it work.
    #searchWrapper {
      position: relative;
    }
    
    #searchResults {
      list-style: none;
      display: none;
      margin: 0;
      padding: 4px;
      position: absolute;
      border: 1px solid;
      box-shadow: 0 1px 2px 2px #ccc;
      top: 100%;
    }
    
    #searchResults a {
      color: inherit;
      text-decoration: none;
      padding: 8px;
      display: block;
    }
    
    #searchResults a:hover,
    #searchResults li.is-focussed a {
      background-color: #eee;
    }
    
    #search:focus ~ #searchResults {
      display: block;
    }
    <div id="searchWrapper">
      <input type="text" id="search" />
      <ul id="searchResults">
        <li focus-index="0"><a href="https://google.com" target="_blank">Result 1</a></li>
        <li focus-index="1"><a href="https://google.com" target="_blank">Result 2</a></li>
      </ul>
    </div>

    I hope this should help.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search