Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

phayes0289's avatar

Filtering a List using Javascript Problem

I am having a problem getting my filtering of a list of names working. I cannot see what is wrong and need some help. I am very new to javascript, so bear with me.

This is my javascript code that includes the filtering code:

$(document).ready(function(){
  updatePersonnel();

  function timeDiffCalc(dateFuture, dateNow) {
    let diffInMilliSeconds = Math.abs(dateFuture - dateNow) / 1000;

    const hours = Math.floor(diffInMilliSeconds / 3600),
          formattedHours = checkTime(hours);
    diffInMilliSeconds -= hours * 3600;

    const minutes = Math.floor(diffInMilliSeconds / 60) % 60,
          formattedMinutes = checkTime(minutes);
    diffInMilliSeconds -= minutes * 60;

    qs('#total_time').value = `${formattedHours||'00'}:${formattedMinutes||'00'}`
  }

  function checkTime(i) {
     return (i < 10) ? "0" + i : i;
  }
  function setTotalTime () {
    var start = $('#startdt').datetimepicker('getValue');
        end = $('#enddt').datetimepicker('getValue');

    if($('#startdt').val() && $('#enddt').val()) timeDiffCalc(end, start)
  }
  setTotalTime();

});
$(".modal-button").on("click", function() {
                // Get the data attributes from the clicked button
                const targetModal = $(this).data("target");
                const inputType = $(this).data("input-type");

                // Call the focusInput function with the inputType and targetModal
                focusInput(inputType, targetModal);
            });
                function focusInput(type) {
                    setTimeout(() => {
                        // Select the input element based on the 'type'
                        let input = (type === 'participants') ? qs("#participants_input") : qs("#instructors_input");
                        // Set focus on the selected input element after a 500ms delay
                        input.focus();
                    }, 500);
}
function clearStorage(type) {
  if(type == 'p_edited') {
    removeTableData('p');
    [...qsa('#p_table input[type="checkbox"]')].forEach(el=>el.checked = false);
  }
  if(type == 't_edited'){
    removeTableData('t');
    [...qsa('#t_table input[type="checkbox"]')].forEach(el=>el.checked = false);
  } 
}

qsa('.modal input[type="checkbox"]').forEach(el => {
  el.setAttribute('autofocus', true);
})

function removeTableData (type) {
    let tableData = qs(`#${type}_selected`);
    while(tableData.hasChildNodes()) {
       tableData.removeChild(tableData.firstChild);
    }
}

$(".filter-input").on("keyup", function(e) {
    console.log("keyup event triggered"); // Added this line for debugging
  var value = $(this).val().toLowerCase();
  $(e.target).closest('.modal-div').find('tr').filter(function() {
    $(this).toggleClass('hidden', $(this).text().toLowerCase().indexOf(value) == -1);
  });

});
$(".filter-input").on("keydown", function(e) {
  let visibleInputs = $(e.target).closest('.modal-div').find('.table-checkboxes tr:visible input');
  if(e.originalEvent.keyCode == 13 && visibleInputs.length == 1) {
    e.preventDefault(); 
    console.log(visibleInputs[0]);
    
    visibleInputs[0].checked = (visibleInputs[0].checked) ? false : true;
    let changeEv = new Event('change');
    visibleInputs[0].dispatchEvent(changeEv);
  }
});

qsa('.modal input[type="checkbox"]').forEach(box=>{
  box.onchange = (e) => {
    let filter = e.target.closest('.modal-div').querySelector('.filter-input');
    filter.value = '';
    filter.focus();

    $(e.target).closest('.modal-div').find('tr').filter(function() {
      $(this).toggle($(this).text().toLowerCase().indexOf('') > -1)
    });

    if(box.checked) {
      addTableRow(box.value, box.closest('table').id);
    } else {
      removeTableRow(box.value, box.closest('table').id);
    }
  }

  $(box).on("keydown", function(e) {
    if(e.originalEvent.keyCode == 13) {
      e.preventDefault(); 
      box.checked = (box.checked) ? false : true;
      let changeEv = new Event('change');
      box.dispatchEvent(changeEv);
    }
  });
})

let focusEl; 
function detectFocus() {
    focusEl = document.activeElement;
    if (focusEl.closest('.modal')) {
      qsa('.table-checkboxes tr').forEach(el=>el.classList.remove('active'));
      if(focusEl.getAttribute('type') == 'checkbox') focusEl.closest('tr').classList.add('active');
    }
}
function attachFocusEvents() {
    window.addEventListener ? window.addEventListener('focus', detectFocus, true) : window.attachEvent('onfocusout', detectFocus); 
}
attachFocusEvents();


let allVisibleBoxes,
    focusIndex;
document.addEventListener('keydown', function(event) {
  if(!focusEl) return false;
  if(focusEl.closest('.modal') && (event.key === 'Tab' || event.key === 'ArrowDown' || event.key === 'ArrowUp')) event.preventDefault();

  if(focusEl.closest('.filter-input')) {
    if((event.shiftKey && event.key === 'Tab') || event.key === 'ArrowUp') {
      return false;
    } else if(event.key === 'Tab' || event.key === 'ArrowDown') {
      allVisibleBoxes = focusEl.nextElementSibling.querySelectorAll('tr:not(.hidden)');
      focusIndex = 0;

      allVisibleBoxes[0].querySelector('input').focus();
    }
  } else if (focusEl.closest('.table-checkboxes input')) {
    if ((event.shiftKey && event.key === 'Tab') || event.key === 'ArrowUp') {
       if(focusIndex == 0) {
          focusEl.closest('.table-checkboxes').previousElementSibling.focus();
       } else {
          focusIndex--;
          allVisibleBoxes[focusIndex].querySelector('input').focus();
       }
    } else if (event.key === 'Tab' || event.key === 'ArrowDown') {
        if(focusIndex == allVisibleBoxes.length - 1) {
          focusEl.closest('.table-checkboxes').previousElementSibling.focus();
        } else {
          focusIndex++;
          allVisibleBoxes[focusIndex].querySelector('input').focus();
        }
    }
  }
  
});

function addTableRow(data,type,id) {
  let table =  (type == 'p_table') ? qs("#p_selected") : qs("#t_selected");

  let tr = `<tr data-value="${data}">
              <td width="14%"><img src="${data.split('|')[3]}" width="50" height="50" class="rounded-circle"/></td>
              <td width="53%">${data.split('|')[1]}</td>
              <td width="33%">${data.split('|')[2]}</td>
            </tr>`;
  table.insertAdjacentHTML( 'beforeend', tr );
}
function removeTableRow(data,type) {
  let table =  (type == 'p_table') ? qs("#p_selected") : qs("#t_selected");
  table.querySelector(`[data-value="${data}"]`).remove();
}

function updatePersonnel() {
  	if(qs('#db_participants').value) qs('#db_participants').value.split(',').forEach(id => {
			let checkbox = qs(`#p_table input[value*="${id}"]`);
			checkbox.checked = true;
			addTableRow(checkbox.value, checkbox.closest('table').id, id);
		})
  
  	if(qs('#db_instructors').value) qs('#db_instructors').value.split(',').forEach(id => {
			let checkbox = qs(`#t_table input[value*="${id}"]`);
			checkbox.checked = true;
			addTableRow(checkbox.value, checkbox.closest('table').id, id);
  	})
}

function qs (selector, searchIn) {
return searchIn ? searchIn.querySelector(selector) : document.querySelector(selector)
}
function qsa (selector, searchIn) {
return searchIn ? searchIn.querySelectorAll(selector) : document.querySelectorAll(selector)
}

This is a list of name code that resides in a modal window on the page:

            {{-- BEGIN PARTICIPANT MODAL WINDOW --}}
            <div class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" id="participants_modal">
                <div class="modal-dialog modal-dialog-right modal-sm">
                    <div class="modal-content">
                        <div class="modal-div">
                            <div class="modal-header">
                                <h2>Participants</h2>
                                <button type="button" onclick="$('#closeMemberWindowEvent').click()"
                                    class="btn btn-secondary" data-dismiss="modal">Close
                                    Window</button>
                            </div>

                            <div>
                                {{-- Begin Included Code --}}
                                <div class="input-group mb-g mr-1">
                                    <input type="text" id="participants_input" 
                                        class="form-control form-control-lg filter-input shadow-inset-2 ml-2 mr-2"
                                        placeholder="Search.." spellcheck="false" autofocus>

                                </div>

                             
                                <table width="100%" class="table-checkboxes table table-bordered" id="p_table">
                                    <tbody>
                                        @php
                                            // Define the default image URL once outside the loop
                                            $defaultImageUrl = URL::asset('/media/images/silhouettes/Firefighter-Silhouette-tiny.jpg');
                                        @endphp
                                        @foreach ($members as $key => $member)
                                            @php
                                                // Check if the member has profile media
                                                $hasProfileMedia = $member->hasMedia('profile');
                                                // Get the profile media URL if it exists
                                                $profileMediaUrl = $hasProfileMedia ? $member->getFirstMediaUrl('profile', 'tiny') : null;
                                            @endphp

                                            <tr>
                                                <td>
                                                    <input type="checkbox" name="participants"
                                                        value="{{ $member->id }}|{{ $member->firstname }} {{ $member->lastname }}|{{ $member->currentAssignment->rank }}|{{ $hasProfileMedia ? $profileMediaUrl : $defaultImageUrl }}" style=" width: 30px;height: 30px; ">
                                                </td>
                                                <td class="m-0 p-0">

                                                    <img src="{{ $hasProfileMedia ? $profileMediaUrl : $defaultImageUrl }}"
                                                        alt="Default Image" name="member" id="member" width="50px">
                                                    {{-- @if ($member->hasMedia('profile'))
                                                    <img src="{{ $member->getFirstMediaUrl('profile','thumb') }}" alt="Default Image" name="member"
                                                        id="member" width="50px">
                                                @else
                                                    <img src="{{ URL::asset('/media/images/silhouettes/Firefighter-Silhouette-tiny.jpg') }}" alt="Default Image"
                                                        name="member" id="member" width="50px">
                                                @endif --}}

                                                </td>
                                                <td>
                                                    <div>
                                                        {{ $member->lastname }}, {{ $member->firstname }}
                                                    </div>
                                                    <div>
                                                        <small>
                                                            {{ $member->currentAssignment->rank ?? 'N/A' }} (Shift
                                                            {{ $member->currentAssignment->shift ?? 'N/A' }},
                                                            {{ $member->currentAssignment->station ?? 'N/A' }})
                                                        </small>
                                                    </div>
                                                </td>
                                            </tr>
                                        @endforeach
                                    </tbody>
                                </table>
                            
                                {{-- End Included Code --}}

                            </div>
                        </div>
                    </div>
                </div>
            </div>
            {{-- END PARTICIANTS MODAL WINDOW --}}
0 likes
4 replies
LaryAI's avatar
Level 58

The problem with the filtering code is that it is not correctly targeting the table rows to filter. Here's the corrected code:

$(".filter-input").on("keyup", function(e) {
  var value = $(this).val().toLowerCase();
  $(e.target).closest('.modal-div').find('table tbody tr').filter(function() {
    $(this).toggleClass('hidden', $(this).text().toLowerCase().indexOf(value) == -1);
  });
});

In the code above, I've added tbody to the selector to specifically target the table rows within the tbody element. This will ensure that only the rows are filtered, and not the table header or any other elements outside the tbody.

Please replace the existing filtering code with the corrected code provided above.

jlrdw's avatar

I agree with @tray2 , I normally use a lookup table with search and pagination if the list could be long, see:

https://laracasts.com/discuss/channels/laravel/can-i-redirect-a-get-request-to-a-post-request-for-inertia-partial-reloads?page=1&replyId=905775

A select option is fine for a few items that won't grow.

Of course you can make a searchable paginating drop down, but I wouldn't.

Note, the example was just done quickly and not styled, etc.

And I know you use livewire, but for things like this, to me regular JS and Fetch JS is so much easier. It's basically similar to a server fetched partial.

Please or to participate in this conversation.