Skip to content

[Bug] initTextFilter throws TypeError when multiple text filters are used #1455

@hhhc

Description

@hhhc

Package

backpack/pro v3.0.3 (also affects earlier versions)

Backpack version

  • backpack/crud 7.0.22
  • backpack/pro 3.0.3
  • PHP 8.3
  • Laravel 12

Bug description

When a CRUD list view has more than one text filter, the page throws a JavaScript error:

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
    at initTextFilter

This breaks the clear button functionality on all text filters except the last one defined.

Steps to reproduce

  1. Create a CrudController with two or more text filters:
$this->crud->addFilter([
    'type' => 'text',
    'name' => 'filter_one',
    'label' => 'Filter One',
], false, function ($value) {
    $this->crud->addClause('where', 'column_one', 'LIKE', "%$value%");
});

$this->crud->addFilter([
    'type' => 'text',
    'name' => 'filter_two',
    'label' => 'Filter Two',
], false, function ($value) {
    $this->crud->addClause('where', 'column_two', 'LIKE', "%$value%");
});
  1. Load the list view in the browser.
  2. Open the browser console — the TypeError appears immediately.

Root cause

In vendor/backpack/pro/resources/views/filters/text.blade.php, the initTextFilter function is pushed via @push('after_scripts') once per text filter. Each push redefines the global initTextFilter function.

The problem is on line 33, where the clear button selector uses a hardcoded Blade variable:

let clearButton = filter.querySelector('.text-filter-{{ $filter->key }}-clear-button');

Since @push appends content and the function is redefined each time, the last text filter's $filter->key wins. When initTextFilter is called for any earlier text filter, querySelector looks for the wrong class name, returns null, and clearButton.addEventListener(...) throws.

Expected behavior

The initTextFilter function should work correctly for all text filters, regardless of how many are on the page.

Suggested fix

Two changes in text.blade.php:

  1. Use the dynamic JS variable filterKey (already extracted from the DOM on line 30) instead of the Blade variable:
- let clearButton = filter.querySelector('.text-filter-{{ $filter->key }}-clear-button');
+ let clearButton = filter.querySelector('.text-filter-' + filterKey + '-clear-button');
  1. Change @push to @pushOnce so the function is only defined once (since it's now fully dynamic, there's no need to redefine it per filter):
- @push('after_scripts')
+ @pushOnce('after_scripts')
  <script>
      function initTextFilter(filter, filterNavbar) {
          ...
      };
  </script>
- @endpush
+ @endPushOnce

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions