Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-70990

Replace jQuery and YUI events core/event with native JS events

    XMLWordPrintable

    Details

    • Testing Instructions:
      Hide

      Changes to get_list_of_plugins() are tested by phpunit
      Most other changes are tested by Behat

      The following tests are to demonstrate that the fallbacks work as expected

      FORM_SUBMIT_AJAX

      1. Apply the attached patch 0001-patchy.patch

        git am ./path/to/0001-patchy.patch
        

      2. Log in as admin
      3. Navigate to Site administration -> Courses -> Course custom fields
      4. Click on 'Add a new category"
      5. Click on "Add a new custom field" in the new category
      6. Choose any type
      7. Open developer tools and open the Console tab
      8. Clear the console
      9. Paste the following:

        Y.use('moodle-core-event', function(Y) {Y.one('form[method=post]').fire(M.core.event.FORM_SUBMIT_AJAX, {currentTarget: Y.one('form[method=post]')});})
        

        1. Confirm that you see the following:

          Caught the YUI M.core.event.FORM_SUBMIT_AJAX event
          Firing the native formSubmittedByJavascript event
          Caught the native formSubmittedByJavascript event
          Stopped processing formSubmittedByJavascript: already handled.
          

      10. Press the "Cancel" button
      11. Click on "Add a new custom field" in the new category
      12. Choose any type
      13. Set a Name, and Short name
      14. Open developer tools and open the Console tab
      15. Clear the console
      16. Press the "Save changes" button
        1. Confirm that you see the following:

          Caught the native formSubmittedByJavascript event
          Firing the YUI M.core.event.FORM_SUBMIT_AJAX event on the form
          Caught the YUI M.core.event.FORM_SUBMIT_AJAX event
          Stopped processing FORM_SUBMIT_AJAX: already handled.
          

          Note: It may be repeated multiple times.

      Legacy events

      1. Open Browser Developer Tools
      2. Open Console and clear it
      3. Paste the following:

        require(['core/event'], ev => ev.getLegacyEvents())
        

        1. Confirm that a message warning that getLegacyEvents has been deprecated
      Show
      Changes to get_list_of_plugins() are tested by phpunit Most other changes are tested by Behat The following tests are to demonstrate that the fallbacks work as expected FORM_SUBMIT_AJAX Apply the attached patch 0001-patchy.patch git am ./path/to/0001-patchy.patch Log in as admin Navigate to Site administration -> Courses -> Course custom fields Click on 'Add a new category" Click on "Add a new custom field" in the new category Choose any type Open developer tools and open the Console tab Clear the console Paste the following: Y.use('moodle-core-event', function(Y) {Y.one('form[method=post]').fire(M.core.event.FORM_SUBMIT_AJAX, {currentTarget: Y.one('form[method=post]')});}) Confirm that you see the following : Caught the YUI M.core.event.FORM_SUBMIT_AJAX event Firing the native formSubmittedByJavascript event Caught the native formSubmittedByJavascript event Stopped processing formSubmittedByJavascript: already handled. Press the "Cancel" button Click on "Add a new custom field" in the new category Choose any type Set a Name, and Short name Open developer tools and open the Console tab Clear the console Press the "Save changes" button Confirm that you see the following : Caught the native formSubmittedByJavascript event Firing the YUI M.core.event.FORM_SUBMIT_AJAX event on the form Caught the YUI M.core.event.FORM_SUBMIT_AJAX event Stopped processing FORM_SUBMIT_AJAX: already handled. Note: It may be repeated multiple times. Legacy events Open Browser Developer Tools Open Console and clear it Paste the following: require(['core/event'], ev => ev.getLegacyEvents()) Confirm that a message warning that getLegacyEvents has been deprecated
    • Affected Branches:
      MOODLE_400_STABLE
    • Pull 3.11 Branch:
      MDL-70990-311
    • Pull Master Branch:
      MDL-70990-master
    • Story Points:
      0
    • Sprint:
      Internationals - 3.11 Sprint 7, Internationals - 3.11 Sprint 8, Internationals - 3.11 Sprint 9

      Description

      The notifiers in the core/event module currently trigger using both jQuery and YUI custom events, however neither of these are compatible with native events:

      • YUI:
        • events are not published natively
        • can only listen to whitelisted native events. Listening to custom native events is non-trivial
        • cannot fire its events natively
        • cannot delegate event listening where a YUI event is fired against a DOM Node.
        • cannot fire events on a DOM Node in a way that they bubble up the DOM, i.e.:

          Y.publish('someEvent', {bubbles: true});
          Y.one(document).on('someEvent' function(e) {
              window.console.log("I was fired");
          });
          Y.one(document).delegate('someEvent', function(e) {
              window.console.log("I was fired");
          });
          Y.one(document.body).fire('someEvent', 'someData');
          // The someEvent custom event will only be fired on the body, and _cannot_ be picked up either by {{.on}} or {{.delegate}} on anything except that _speciifc_ node, no matter how it is published.
          

      • jQuery:
        • events are not published natively
        • can listen and respond to native events
        • puts event arguments into a second argument of the function call, i.e.

          $(document).on('someEvent', function(e, detail) {
              window.console.log(detail); // abracadabra
          });
           
          $(document.body).trigger('someEvent', 'abracadabra');
          

        • contain additional helper functions, including isDefaultPrevented()
      • Native ES:
        • events are triggered on a NodElement
        • events bubble up the DOM
        • events can cross the ShadowDOM boundary
        • event arguments are provided in the Event.detail:

          document.addEventListener('someEvent', e => window.console.log(e.detail)); // abracadabra
          document.body.dispatchEvent(new CustomEvent('someEvent', {bubbles: true, detail: 'abracadabra'}));
          

        • provides no way to have a second argument to the event handler
        • provides cancelable events, but these are checked via the defaultPrevented readonly boolean var

      Moving forward we need to standardise our use of events in Moodle and the only sensible approach is to make use of Native events across the board. These can be listened to by YUI, jQuery, and native JS and are available in all supported browsers natively. They can be configured to bubble up the DOM, cross the ShadowDOM, be cancelled, and of course they follow the ES specification.

      Sadly this is not a straightforward transition as:

      • YUI events are largely isolated from the DOM Events:
        • An event fired natively does not fire as a YUI Event
        • An event fired in YUI does not fire natively
      • Some jQuery events can be transitioned, but:
        • An event fired in jQuery does not fire natively; and
        • Most of our jQuery events fire with a second param. This is the extraParameters var in https://api.jquery.com/trigger/ - it is not possible to pass additional parameters to native events and all extra additional data should be in the Event.detail

      To do this we will have to:

      • create new event names (because jQuery listeners pick up native events)
      • update the internal uses of the events we know about to call the new events
      • create listeners on the new events to trigger the old events

      We are lucky in that several of the jQuery events that we have are only triggered in core via a notify function, i.e. notifyFormSubmitted and not with the events being fired directly. This means that we can retrofit these triggers to use native events easily. We are even luckier in that all but one of these takes no arguments, so we do not have to dance with moving the second argument to e.detail.

      The problems that we will have to solve are:

      • Moving the event detail from the second argument to e.detail for that one case
      • Firing the YUI events where the previous event was fired on a specific node (that same case)
      • Providing b/c for a contrib module firing a YUI event - this must be configured to additionally fire the native event
      • preventing recursion of YUI -> native -> YUI -> native -> YUI -> etc

      In order to solve the case with a specific node, we can make use of Mutation Observers to add the appropriate b/c triggers.

      We also have an issue whereby we have a central core/event AMD module which includes events for a number of events relating to other subsystems. We should be creating an events module in the relevant subsystem instead to ensure both discoverability and compliance with the cross-component coding rules.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              dobedobedoh Andrew Nicols
              Reporter:
              dobedobedoh Andrew Nicols
              Peer reviewer:
              Mathew May
              Participants:
              Component watchers:
              Andrew Nicols, Dongsheng Cai, Huong Nguyen, Jun Pataleta, Michael Hawkins, Shamim Rezaie, Simey Lameze
              Votes:
              1 Vote for this issue
              Watchers:
              9 Start watching this issue

                Dates

                Created:
                Updated:

                  Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0 minutes
                  0m
                  Logged:
                  Time Spent - 2 days, 3 hours, 50 minutes
                  2d 3h 50m