Refactor (if feasible) uses of the jQuery trigger function to use vanillaJS

Created on 24 September 2021, almost 3 years ago
Updated 18 April 2024, 2 months ago

Problem/Motivation

As mentioned in the parent issue #3238306: [META] Where possible, refactor existing jQuery uses to vanillaJS to reduce jQuery footprint β†’ , we are working towards reducing our jQuery footprint. One of the ways to accomplish this is to reduce the number of jQuery features used in Drupal core. We have added eslint rules that identify specific features and fail tests when those features are in use.

There are (or will be) individual issues for each jQuery-use eslint rule. This one is specific to jquery/no-trigger, which targets the jQuery trigger function.

Steps to reproduce

Proposed resolution

Remaining tasks

  • In core/.eslintrc.jquery.json Change "jquery/no-trigger": 0, to "jquery/no-trigger": 2, to enable eslint checks for uses of jQuery .trigger(). With this change, you'll be able to see uses of the undesirable jQuery feature by running yarn lint:core-js-passing from the core directory
  • Add the following lines to core/scripts/dev/commit-code-check.sh so the DrupalCI testing script can catch this jQuery usage on all files, not just those which have changed
    # @todo Remove the next chunk of lines before committing. This script only lints
    #  JavaScript files that have changed, so we add this to check all files for
    #  jQuery-specific lint errors.
    cd "$TOP_LEVEL/core"
    node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json .
    
    CORRECTJQS=$?
    if [ "$CORRECTJQS" -ne "0" ]; then
      # No need to write any output the node command will do this for us.
      printf "${red}FAILURE ${reset}: unsupported jQuery usage. See errors above."
      STATUS=1
      FINAL_STATUS=1
    fi
    cd $TOP_LEVEL
    # @todo end lines to remove

    Add the block about 10 lines before the end of the file, just before if [[ "$FINAL_STATUS" == "1" ]] && [[ "$DRUPALCI" == "1" ]]; then, then remove it once all the jQuery uses have been refactored.

  • If it's determined to be feasible, refactor those uses of jQuery .trigger() to use Vanilla (native) JavaScript instead.

Files to fix | Usage count

  • core/misc/ajax.js (5)
  • core/misc/details.js (1)
  • core/misc/dialog/dialog.ajax.js (5)
  • core/misc/dialog/dialog.jquery-ui.js (1)
  • core/misc/dialog/dialog.js (4)
  • core/misc/dialog/dialog.position.js (1)
  • core/misc/dialog/off-canvas/js/off-canvas.js (2)
  • core/misc/displace.js (1)
  • core/misc/form.js (3)
  • core/misc/machine-name.js (1)
  • core/misc/states.js (2)
  • core/misc/tabbingmanager.js (5)
  • core/misc/tabledrag.js (4)
  • core/misc/tableresponsive.js (2)
  • core/misc/tableselect.js (2)
  • core/misc/vertical-tabs.js (1)
  • core/modules/contextual/js/contextual.js (1)
  • core/modules/editor/js/editor.admin.js (3)
  • core/modules/editor/js/editor.dialog.js (1)
  • core/modules/editor/js/editor.js (1)
  • core/modules/field_ui/field_ui.js (3)
  • core/modules/file/file.js (3)
  • core/modules/filter/filter.js (1)
  • core/modules/media_library/js/media_library.click_to_select.js (1)
  • core/modules/media_library/js/media_library.ui.js (3)
  • core/modules/media_library/js/media_library.view.js (1)
  • core/modules/menu_ui/menu_ui.js (3)
  • core/modules/node/node.preview.js (1)
  • core/modules/settings_tray/js/settings_tray.js (5)
  • core/modules/system/js/system.date.js (1)
  • core/modules/system/js/system.js (1)
  • core/modules/text/text.js (1)
  • core/modules/toolbar/js/toolbar.js (3)
  • core/modules/toolbar/tests/src/Nightwatch/Tests/toolbarTest.js (4)
  • core/modules/tour/js/tour.js (1)
  • core/modules/views_ui/js/ajax.js (2)
  • core/modules/views_ui/js/dialog.views.js (1)
  • core/modules/views_ui/js/views-admin.js (6)
  • core/tests/Drupal/Nightwatch/Tests/tabbableShimTest.js (2)
  • core/themes/claro/js/details.js (1)
  • core/themes/claro/js/nav-tabs.js (1)

User interface changes

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Needs work

Version

11.0 πŸ”₯

Component
JavascriptΒ  β†’

Last updated about 8 hours ago

Created by

πŸ‡ΊπŸ‡ΈUnited States hooroomoo

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    So part of this issue is about having a linting rule put in place to ensure that new code added to Drupal does not implement jQuery's .trigger method AND the same rule provides a nice checklist of all the places where Drupal core is currently implementing that method. That's a good piece of foundational / platform support for ensuring we're reducing our jQuery footprint.

    It's also part of, but not all of, the main goal here. The main goal is to actually reduce jQuery's footprint by removing all the instances of the method. Once that's done, we can close the door on adding new code that uses that part of jQuery. Then we're done.

    In my opinion, we are currently unblocked from making progress in that area. So i'm going to hack on this idea a bit.

  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    Current output of yarn lint:core-js-passing when the jquery/no-trigger rule is enabled:

    /var/www/html/web/core/misc/ajax.js
       584:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       585:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       587:17  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       744:7   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      1101:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/details.js
      21:5  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/dialog/dialog.ajax.js
       38:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       42:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      102:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      102:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      102:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/dialog/dialog.jquery-ui.js
      72:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/dialog/dialog.js
      73:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      76:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      80:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      84:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/dialog/dialog.position.js
      125:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/dialog/off-canvas/js/off-canvas.js
      142:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      244:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/displace.js
      222:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/form.js
      147:5   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      292:7   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      298:24  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/machine-name.js
      190:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/states.js
      735:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      746:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/tabbingmanager.js
      181:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      248:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      367:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      382:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      396:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/tabledrag.js
      395:5   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      610:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      665:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      735:7   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/tableresponsive.js
       49:5   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      186:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/tableselect.js
      64:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      86:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/misc/vertical-tabs.js
      173:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/contextual/js/contextual.js
      135:5  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/editor/js/editor.admin.js
      30:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      46:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      65:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/editor/js/editor.dialog.js
      32:5  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/editor/js/editor.js
      305:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/field_ui/field_ui.js
       50:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       63:15  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      338:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/file/file.js
      198:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      214:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      278:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/filter/filter.js
      28:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/media_library/js/media_library.click_to_select.js
      29:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/media_library/js/media_library.ui.js
       65:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      336:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      373:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/media_library/js/media_library.view.js
      30:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/menu_ui/menu_ui.js
      80:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      81:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      90:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/node/node.preview.js
      97:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/settings_tray/js/settings_tray.js
       36:5   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       43:5   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      100:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      209:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      209:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/system/js/system.date.js
      60:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/system/js/system.js
      79:7  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/text/text.js
      68:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/toolbar/js/toolbar.js
      177:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      180:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      183:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/toolbar/tests/src/Nightwatch/Tests/toolbarTest.js
      241:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      254:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      304:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      308:9  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/tour/js/tour.js
      39:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/views_ui/js/ajax.js
      108:7   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      177:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/views_ui/js/dialog.views.js
      54:11  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/modules/views_ui/js/views-admin.js
       480:7   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       581:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
       622:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      1180:7   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      1246:9   error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      1259:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/tests/Drupal/Nightwatch/Tests/tabbableShimTest.js
      180:22  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
      180:22  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/themes/claro/js/details.js
      24:13  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
    /var/www/html/web/core/themes/claro/js/nav-tabs.js
      71:5  error  Prefer dispatchEvent to $.trigger  jquery/no-trigger
    
  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul
  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    To fix, this guidance from stack overflow seems important: https://stackoverflow.com/questions/5658849/whats-the-equivalent-of-jque...

    In this article, they (as as the linting error response) recommend using JavaScript's dispatchEvent method on the targeted element. What the stack overflow article mentions is that we can avoid potential problems by being thoughtful about what kind of Event we dispatch. Considering the careful creation and use of JavaScript Events, we are likely to discover that each use of .trigger('specific_event') has a sensible default replacement.

    Let's create a list of all the different kinds of events we're currently triggering and figure out a dispatchEvent equivalent.

  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    Found .triggers (excluding 3rd party js)

    • focus
    • formUpdated.machineName
    • ajaxSuccess
    • ajaxComplete
    • ajaxStop
    • summaryUpdated
    • click
    • mousedown
    • mouseup
    • dialog:beforecreate
    • dialog:aftercreate
    • dialog:beforeclose
    • dialog:afterclose
    • dialogContentResize
    • resize.dialogResize
    • resize.off-canvas
    • dialogContentResize.off-canvas
    • drupalViewportOffsetChange
    • summaryUpdated
    • formUpdated
    • formFragmentLinkClickOrHashChange
    • change
    • drupalTabbingConstrained
    • DrupalTabbingContextReleased
    • DrupalTabbingContextActivated
    • DrupalTabbingContextDeactivated
    • blur
    • drupalEditorFeatureAdded
    • drupalEditorFeatureRemoved
    • drupalEditorFeatureModified
    • editor:dialogsave
    • fileUpload
    • change.filterGuidelines
    • submit
    • resize.tabs

    A lot of these seem to be custom events. jQuery enumerates it's events as:

    • blur
    • change
    • click
    • contextmenu
    • dblclick
    • error
    • focus
    • hover
    • keydown
    • keypress
    • keyup
    • load
    • mousedown
    • mouseenter
    • mouseleave
    • mousemove
    • mouseout
    • mouseover
    • mouseoff
    • mouseup
    • resize
    • scroll
    • select
    • submit
    • unload
  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    When we use jQuery's list of events to reduce our list of found usages of .trigger we can reduce the list to:

    • custom events we create
    • focus
    • click
    • mousedown
    • mouseup
    • change
    • blur
    • submit
    • resize?

    Tricky bit might come into play when we need to stop using .trigger for jQuery events that we are overriding / extending. Best place to see what I mean here is to look at ajaxSuccess (all the ajax* "Events")

    from ajax.es6.js, line 1768

    $.extend(true, $.event.special, {
        ajaxSuccess: {
          trigger(event, xhr, settings) {
            if (stopEvent(xhr, settings)) {
              return false;
            }
          },
        },
        ajaxComplete: {
          trigger(event, xhr, settings) {
            if (stopEvent(xhr, settings)) {
              // jQuery decrements its internal active ajax counter even when we
              // stop the ajaxComplete event, but we don't want that counter
              // decremented, because for our purposes this request is still active
              // while commands are executing. By incrementing it here, the net
              // effect is that it remains unchanged. By remaining above 0, the
              // ajaxStop event is also prevented.
              $.active++;
              return false;
            }
          },
        },
      });
    
  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    Scanning core for all instances of $.extend returns > 50 files. Perhaps that's not a good search at the moment. Maybe we should take a stab at fixing these "easy" replacements and see how much is left aftwards.

    To that end, how can we map the current "easy" uses .trigger to non-jquery solutions. In order to understand better how to provide developers a good replacement for what they currently have available with jQuery's event system let's focus on Focus.

    Possible deprecation code comment / guidance we could provide

    .trigger('focus') => .dispatchEvent(new FocusEvent('focus'))
    

    Using an event object instead of doing something like $element.focus() allows the event system to properly bubble / propagate the event through the system.

    When we use an Event object, JavaScript's event dispatcher triggers the event, allows any event handlers to do their actions, and when they're all done, finally focuses on the targeted element.

    When .focus() is used, the targeted element immediately receives focus then the event handlers get notified of the event and they do their actions.

  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    More research also uncovered a possible solution for the CustomEvent concern. Regarding:

    Anything event related such as trigger may have a little bit of additional complexity as we need to account for event listeners in contrib modules that expect the [, extraParameters ] arg http://api.jquery.com/trigger/. I'm pretty sure there are reasonable ways to hack around it though, and there's only upside to getting started on this even if that part isn't figured out yet.

    Even when we're using only JavaScript to define events we'll need to allow those events to define custom data to consume. We can do so using an Event's detail property:

    // Create a custom event with type 'myEvent' and include custom options
    var customEvent = new CustomEvent('myEvent', {
      detail: {
        options: [
          {
            name: "Option 1",
            value: "1"
          },
          {
            name: "Option 2",
            value: "2"
          }
        ]
      }
    });
    
    // Dispatch the custom event
    document.getElementById("myButton").dispatchEvent(customEvent);
    
  • πŸ‡ΊπŸ‡ΈUnited States cosmicdreams Minneapolis/St. Paul

    Considering all of the above, it appears that we're unblocked here. All this groundwork is all the time I have today to explore this question. I'll see if I can get back to this tonight to start hacking on a MR for some of these easy bits.

  • πŸ‡³πŸ‡±Netherlands eelkeblok Netherlands πŸ‡³πŸ‡±

    Maybe this is a silly question, but is this backwards compatible? How to jQuery events compare to events triggered through dispatchEvent()?

  • Status changed to Needs work 4 months ago
  • Pipeline finished with Failed
    4 months ago
    Total: 7120s
    #102182
  • Pipeline finished with Failed
    4 months ago
    Total: 2941s
    #106926
  • Pipeline finished with Failed
    4 months ago
    Total: 496s
    #111824
  • Pipeline finished with Failed
    4 months ago
    Total: 487s
    #112060
  • Pipeline finished with Failed
    4 months ago
    Total: 2476s
    #113035
  • Pipeline finished with Failed
    4 months ago
    Total: 485s
    #116944
  • Pipeline finished with Failed
    4 months ago
    Total: 493s
    #117978
  • Pipeline finished with Failed
    4 months ago
    Total: 579s
    #118975
  • Pipeline finished with Failed
    3 months ago
    Total: 1004s
    #124204
  • Pipeline finished with Failed
    3 months ago
    Total: 535s
    #125929
  • Pipeline finished with Failed
    3 months ago
    Total: 690s
    #129616
  • Merge request !7342Resolve #3239127 "Refactor triggerv2" β†’ (Open) created by Sam Phillemon
  • Sam Phillemon β†’ changed the visibility of the branch 3239127-refactor-trigger to hidden.

  • Pipeline finished with Failed
    3 months ago
    Total: 630s
    #138324
  • Pipeline finished with Failed
    3 months ago
    Total: 752s
    #140777
  • Pipeline finished with Failed
    2 months ago
    Total: 721s
    #147234
  • Pipeline finished with Failed
    2 months ago
    Total: 720s
    #148229
  • I am facing issues with the PHP unit tests. I have tried fixing some of them but it is causing others to fail. It would be great if someone else could have a look at it.

Production build 0.69.0 2024