diff --git a/addons/website/static/src/snippets/s_table_of_content/options.js b/addons/website/static/src/snippets/s_table_of_content/options.js
index b75a41145e7..9fb04952dde 100644
--- a/addons/website/static/src/snippets/s_table_of_content/options.js
+++ b/addons/website/static/src/snippets/s_table_of_content/options.js
@@ -19,6 +19,8 @@ options.registry.TableOfContent = options.Class.extend({
const config = {attributes: false, childList: true, subtree: true, characterData: true};
this.observer = new MutationObserver(() => this._generateNav());
this.observer.observe(targetNode, config);
+ // The mutation observer doesn't observe the attributes change, it would
+ // be too much. Adding content_changed "listener" instead.
this.$target.on('content_changed', () => this._generateNav());
return this._super(...arguments);
},
@@ -89,11 +91,20 @@ options.registry.TableOfContent = options.Class.extend({
return;
}
this.options.wysiwyg && this.options.wysiwyg.odooEditor.unbreakableStepUnactive();
+ const navEl = this.$target[0].querySelector('.s_table_of_content_navbar');
const headingsEls = this.$target.find(this.targetedElements).toArray()
.filter(el => !el.closest('.o_snippet_desktop_invisible'));
const areHeadingsEqual = this.oldHeadingsEls.length === headingsEls.length
&& this.oldHeadingsEls.every((el, i) => el.isEqualNode(headingsEls[i]));
- if (areHeadingsEqual) {
+ const areVisibilityIdsEqual = headingsEls.every((headingEl) => {
+ const visibilityId = headingEl.closest('section').getAttribute('data-visibility-id');
+ const matchingLinkEl = navEl.querySelector(`a[href="#${headingEl.getAttribute('id')}"]`);
+ const matchingLinkVisibilityId = matchingLinkEl ? matchingLinkEl.getAttribute('data-visibility-id') : null;
+ // Check if visibilityId matches matchingLinkVisibilityId or both
+ // are null/undefined
+ return visibilityId === matchingLinkVisibilityId;
+ });
+ if (areHeadingsEqual && areVisibilityIdsEqual) {
// If the content of the navbar before the change of the DOM is
// equal to the content of the navbar after the change of the DOM,
// then there is no need to regenerate the navbar.
@@ -106,15 +117,15 @@ options.registry.TableOfContent = options.Class.extend({
}
// We dispose the scrollSpy because the navbar will be updated.
this._disposeScrollSpy();
- const $nav = this.$target.find('.s_table_of_content_navbar');
- $nav.empty();
+ navEl.innerHTML = '';
headingsEls.forEach((el) => {
const $el = $(el);
const id = uniqueId("table_of_content_heading_" + new Date().getTime() + "_");
- $('').attr('href', "#" + id)
+ const visibilityId = $el.closest('section').attr('data-visibility-id');
+ $('').attr({ 'href': "#" + id, 'data-visibility-id': visibilityId })
.addClass('table_of_content_link list-group-item list-group-item-action py-2 border-0 rounded-0')
.text($el.text())
- .appendTo($nav);
+ .appendTo(navEl);
$el.attr('id', id);
$el[0].dataset.anchor = 'true';
});