0

Background

I have a filtered view for the content type "Change Requests" that I built as a page, and as a block. The block is added to the user's home page and only shows that user's change requests. The filters div was always visible, but I wanted it to be collapsed and to have a button which expands the filters when the user wants to change it.

I added a button, some custom styling to show/hide the filters block, and a jQuery file that finds the button ID and adds an event listener to it. This worked successfully at first, but then I noticed that when you refresh the home page, sometimes the event listeners are not added to the block and it does not function at all. The view page on the other hand works seemingly every time.

This seems to work sometimes but not other times. For example, sometimes the home page loads and the functionality works, but other times it does not. If I refresh the page a few times, it will load at least once without the event listener added. This also happens when I click the home page link or on first time load, and not just when I am refreshing the page.

Setup

All three steps were used to alter or create files within my custom theme folder.

  1. Altered views-exposed-form.html.twig and added a button after the filters div:
<div class="form--inline clearfix filters">
  {{ form }}
</div>
<button id="expand-filters" type="button" class="button" aria-pressed="false">filters</button>
  1. Added some styling in the CSS to hide/show filters:
.filters {
  display: none;
}
.filters.visible {
  display: block;
}
  1. Created a jQuery file that adds event listener to button to show/hide the filters div:
jQuery(document).ready(function($) {
    // Expand Filter Button
    $('#expand-filters').on('click', function() {
        $(this).toggleClass('active');
        $(this).attr('aria-pressed', $(this).attr('aria-pressed') === 'true' ? 'false' : 'true');
        $('.filters').toggleClass('visible');
    });
});

It seems to me that sometimes the files either don't execute after the document loads, or they execute before elements are loaded and thus fail to add the event listeners. That is my guess, as I don't fully understand why this isn't working in the block but is working in the view.

Question

What is the right way to add this functionality so that it works every time, and not intermittently? I am open to solutions which add modules, but my preferred method is to add this functionality within the theme so that it works only by loading my custom theme into pages.

Additional Info

This site is using Drupal 10 so I am trying to use any and all conventions that apply to Drupal 10.

6
  • Have you got Big Pipe enabled? If so, try turning it off and testing again. It's still a little flaky in D10 and I had to work round a similar issue with it just yesterday. Nov 17, 2023 at 20:15
  • It turns out that I do. Do you mean to go to the extend menu and uninstall it? Nov 17, 2023 at 21:01
  • For Drupal+Javascript follow the instructions here drupal.org/docs/drupal-apis/javascript-api/…. Then BigPipe is not flaky, see drupal.stackexchange.com/questions/310441/…
    – 4uk4
    Nov 18, 2023 at 10:21
  • I agree, you should be using Drupal.behaviors to add JavaScript in most situations.
    – mona lisa
    Nov 18, 2023 at 16:04
  • Sorry, I missed the obvious - which was that the JS wasn't following good practice. The issues with Big Pipe have somewhat preoccupied me recently and I was blindsided! Nov 18, 2023 at 17:34

1 Answer 1

0

After some research and help from the comments under my question, I found a solution that seems to work.

  1. Use Drupal Javascript best practices

However, when I tried these instructions alone, I got the error:

Uncaught TypeError: $(...).once is not a function

I believe this is due to jQuery.once() being removed as of D10 and replaced with core/once

  1. After further research, I found a very helpful post about how to Replace jQuery.once() with Javascript once().

Just to use a better example than in my above question, consider this jQuery:

jQuery(document).ready(function($) {
    // Expand Filter Button
    $('#expand-filters').on('click', function() {
        $(this).toggleClass('active');
        $(this).attr('aria-pressed', $(this).attr('aria-pressed') === 'true' ? 'false' : 'true');
        $('.filters').toggleClass('visible');
    });
});

Changing it to Drupal Behaviors looks like this:

(function ($, Drupal) {
  Drupal.behaviors.filterBehavior = {
    attach: function (context, settings) {
      $(once('filterBehavior', '#expand-filters', context)).each(function () {
        $(this).on('click', function(e) {
          $(this).toggleClass('active');
          $(this).attr('aria-pressed', $(this).attr('aria-pressed') === 'true' ? 'false' : 'true');
          $('.filters').toggleClass('visible');
        })
      })
    }
  };
})(jQuery, Drupal);

And in my theme.libraries.yml file, I added core/once to the dependencies:

dependencies:
    - core/drupal
    - core/jquery
    - core/once

Following these changes, I just cleared the caches and the issue seems to now be fixed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.