From dd5668a6387a99c0f45d37088f62781fd309c510 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 8 May 2026 14:03:43 +0200 Subject: [PATCH] js: Do not trigger `rendered` for unchanged content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The request object now holds a set that is populated with elements that were updated during ´onResponse`. `onComplete` now uses it to trigger the `rendered` event on each entry. Note that for multipart updates the way the event is triggered has changed slightly. Since multipart updates usually do not contain nested containers, the updated parts never triggered the event and only the main container did. I figured this might be useful, but it isn't necessary to fix the referenced issue. fixes #5377 --- doc/80-Upgrading.md | 11 +++++++++++ public/js/icinga/loader.js | 23 +++++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/doc/80-Upgrading.md b/doc/80-Upgrading.md index e03952b745..364dd826f9 100644 --- a/doc/80-Upgrading.md +++ b/doc/80-Upgrading.md @@ -3,6 +3,17 @@ Specific version upgrades are described below. Please note that upgrades are incremental. An upgrade from v2.6 to v2.8 requires to follow the instructions for v2.7 too. +## Upgrading to Icinga Web x.xx + +**Framework changes affecting third-party code** + +* Our JavaScript framework does not trigger the `rendered` event for content that didn't update anymore. In case + `beforerender` triggers, it is still guaranteed that `rendered` will be triggered. But listeners should still + be inspected to ensure compatibility. +* The JavaScript framework triggers the `rendered` event on content now that changes due to a multipart update. + Previously, the event was only triggered on the related main container. Any listener that guards itself with + `event.currentTarget === event.target` in order to ignore bubbled events will still behave as expected. + ## Upgrading to Icinga Web 2.14 **Framework changes affecting third-party code** diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 6e7d7f2894..e451b7f8da 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -344,6 +344,7 @@ req.$target = $target; req.$redirectTarget = $target; + req.renderTargets = new Set(); req.url = url; req.done(this.onResponse); req.fail(this.onFailure); @@ -856,6 +857,17 @@ forceFocus = req.forceFocus; } + req.renderTargets.add($target[0]); + const container = $target[0].closest('.container'); + if (container !== null) { + if (req.renderTargets.has(container)) { + // Ensure parent containers are processed last + req.renderTargets.delete(container); + } + + req.renderTargets.add(container); + } + _this.renderContentToContainer( match[3], $target, @@ -875,6 +887,9 @@ } }) } else { + req.$target[0].querySelectorAll(':scope .container').forEach((c) => req.renderTargets.add(c)); + req.renderTargets.add(req.$target[0]); + this.renderContentToContainer( req.responseText, req.$target, @@ -1027,10 +1042,10 @@ // Lazy load module javascript (Applies only to module.js code) this.icinga.ensureSubModules(req.$target); - req.$target.find('.container').each(function () { - $(this).trigger('rendered', [req.autorefresh, req.scripted, req.autosubmit]); - }); - req.$target.trigger('rendered', [req.autorefresh, req.scripted, req.autosubmit]); + for (const target of req.renderTargets) { + this.icinga.logger.debug('Triggering rendered event for target', target); + $(target).trigger('rendered', [req.autorefresh, req.scripted, req.autosubmit]); + } this.icinga.ui.refreshDebug(); },