diff --git a/django/contrib/admin/static/admin/js/autocomplete.js b/django/contrib/admin/static/admin/js/autocomplete.js index 6095abe233..d3daeab890 100644 --- a/django/contrib/admin/static/admin/js/autocomplete.js +++ b/django/contrib/admin/static/admin/js/autocomplete.js @@ -27,9 +27,7 @@ $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2(); }); - $(document).on('formset:added', (function() { - return function(event, $newFormset) { - return $newFormset.find('.admin-autocomplete').djangoAdminSelect2(); - }; - })(this)); + document.addEventListener('formset:added', (event) => { + $(event.target).find('.admin-autocomplete').djangoAdminSelect2(); + }); } diff --git a/django/contrib/admin/static/admin/js/inlines.js b/django/contrib/admin/static/admin/js/inlines.js index 101fdc7b68..e9a1dfe122 100644 --- a/django/contrib/admin/static/admin/js/inlines.js +++ b/django/contrib/admin/static/admin/js/inlines.js @@ -88,7 +88,12 @@ if (options.added) { options.added(row); } - $(document).trigger('formset:added', [row, options.prefix]); + row.get(0).dispatchEvent(new CustomEvent("formset:added", { + bubbles: true, + detail: { + formsetName: options.prefix + } + })); }; /** @@ -130,7 +135,11 @@ if (options.removed) { options.removed(row); } - $(document).trigger('formset:removed', [row, options.prefix]); + document.dispatchEvent(new CustomEvent("formset:removed", { + detail: { + formsetName: options.prefix + } + })); // Update the TOTAL_FORMS form count. const forms = $("." + options.formCssClass); $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); diff --git a/docs/ref/contrib/admin/javascript.txt b/docs/ref/contrib/admin/javascript.txt index 05731c57b0..ce94176691 100644 --- a/docs/ref/contrib/admin/javascript.txt +++ b/docs/ref/contrib/admin/javascript.txt @@ -8,15 +8,15 @@ Inline form events ================== You may want to execute some JavaScript when an inline form is added or removed -in the admin change form. The ``formset:added`` and ``formset:removed`` jQuery -events allow this. The event handler is passed three arguments: +in the admin change form. The ``formset:added`` and ``formset:removed`` events +allow this. ``event.detail.formsetName`` is the formset the row belongs to. +For the ``formset:added`` event, ``event.target`` is the newly added row. -* ``event`` is the ``jQuery`` event. -* ``$row`` is the newly added (or removed) row. -* ``formsetName`` is the formset the row belongs to. +.. versionchanged:: 4.1 -The event is fired using the :ref:`django.jQuery namespace -<contrib-admin-jquery>`. + In older versions, the event was a ``jQuery`` event with ``$row`` and + ``formsetName`` parameters. It is now a JavaScript ``CustomEvent`` with + parameters set in ``event.detail``. In your custom ``change_form.html`` template, extend the ``admin_change_form_document_ready`` block and add the event listener code: @@ -34,17 +34,14 @@ In your custom ``change_form.html`` template, extend the .. code-block:: javascript :caption: app/static/app/formset_handlers.js - (function($) { - $(document).on('formset:added', function(event, $row, formsetName) { - if (formsetName == 'author_set') { - // Do something - } - }); - - $(document).on('formset:removed', function(event, $row, formsetName) { - // Row removed - }); - })(django.jQuery); + document.addEventListener('formset:added', (event) => { + if (event.detail.formsetName == 'author_set') { + // Do something + } + }); + document.addEventListener('formset:removed', (event) => { + // Row removed + }); Two points to keep in mind: @@ -53,29 +50,3 @@ Two points to keep in mind: * ``{{ block.super }}`` is added because Django's ``admin_change_form_document_ready`` block contains JavaScript code to handle various operations in the change form and we need that to be rendered too. - -Sometimes you'll need to work with ``jQuery`` plugins that are not registered -in the ``django.jQuery`` namespace. To do that, change how the code listens for -events. Instead of wrapping the listener in the ``django.jQuery`` namespace, -listen to the event triggered from there. For example: - -.. code-block:: html+django - - {% extends 'admin/change_form.html' %} - {% load static %} - - {% block admin_change_form_document_ready %} - {{ block.super }} - <script src="{% static 'app/unregistered_handlers.js' %}"></script> - {% endblock %} - -.. code-block:: javascript - :caption: app/static/app/unregistered_handlers.js - - django.jQuery(document).on('formset:added', function(event, $row, formsetName) { - // Row added - }); - - django.jQuery(document).on('formset:removed', function(event, $row, formsetName) { - // Row removed - }); diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 88f4069f40..46d0cf3540 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -412,6 +412,10 @@ Miscellaneous * The ``type="text/css"`` attribute is no longer included in ``<link>`` tags for CSS :doc:`form media </topics/forms/media>`. +* ``formset:added`` and ``formset:removed`` JavaScript events are now pure + JavaScript events and don't depend on jQuery. See + :ref:`admin-javascript-inline-form-events` for more details on the change. + .. _deprecated-features-4.1: Features deprecated in 4.1 diff --git a/js_tests/admin/inlines.test.js b/js_tests/admin/inlines.test.js index 550add2fce..ee8567dbf4 100644 --- a/js_tests/admin/inlines.test.js +++ b/js_tests/admin/inlines.test.js @@ -38,26 +38,20 @@ QUnit.test('added form has remove button', function(assert) { }); QUnit.test('add/remove form events', function(assert) { - assert.expect(6); - const $ = django.jQuery; - const $document = $(document); + assert.expect(5); const addButton = this.table.find('.add-row a'); - $document.on('formset:added', function(event, $row, formsetName) { + document.addEventListener('formset:added', (event) => { assert.ok(true, 'event `formset:added` triggered'); - assert.equal(true, $row.is('#first-1')); - assert.equal(formsetName, 'first'); - $document.off('formset:added'); - }); + assert.equal(true, event.target.matches('#first-1')); + assert.equal(event.detail.formsetName, 'first'); + }, {once: true}); addButton.click(); - const deletedRow = $('#first-1'); const deleteLink = this.table.find('.inline-deletelink'); - $document.on('formset:removed', function(event, $row, formsetName) { + document.addEventListener('formset:removed', (event) => { assert.ok(true, 'event `formset:removed` triggered'); - assert.equal(true, $row.is(deletedRow)); - assert.equal(formsetName, 'first'); - $document.off('formset:removed'); - }); - deleteLink.trigger($.Event('click', {target: deleteLink})); + assert.equal(event.detail.formsetName, 'first'); + }, {once: true}); + deleteLink.click(); }); QUnit.test('existing add button', function(assert) {