buddy_tooltips: Destroy tooltips if reference is not visible.

We destroy the tooltips for which `reference` was either removed
from DOM or is hidden.

We only need to do this for tooltips
contained in simplebar containers for which tooltips can
overflow the boundary of the simplebar container.

There are 4 approaches we could have done this:
1. Asked tippy.js maintainers to do this for us.
In https://github.com/atomiks/tippyjs/issues/938 the
maintainer said that it is the responsibility of
the user to do so.

2. Tracked whenever we update the DOM for such elements and hide
tooltips when we were hiding the `reference` elements. This had
various problems like it is hard trigger events when certain elements have
been removed from DOM when `html()` method is used to render
new content.

3. Run an `optimized` periodic job to destroy tooltips when
`reference` elements are hidden. This isn't a good method to
do this since it sucks power and adds latency.

4. Use a `MutationObserver` on the parent element and watch
for changes. This methods seems to work well with no bad
side effects. We use this approach.
This commit is contained in:
Aman Agrawal
2021-04-10 13:30:39 +00:00
committed by Tim Abbott
parent fc18ec4c04
commit 3410ff2e64

View File

@@ -529,6 +529,7 @@ export function initialize() {
function do_render_buddy_list_tooltip(elem, title_data) {
let placement = "left";
let observer;
if (window.innerWidth < media_breakpoints_num.md) {
// On small devices display tooltips based on available space.
// This will default to "bottom" placement for this tooltip.
@@ -546,6 +547,32 @@ export function initialize() {
showOnCreate: true,
onHidden: (instance) => {
instance.destroy();
observer.disconnect();
},
onShow: (instance) => {
// For both buddy list and top left corner pm list, `target_node`
// is their parent `ul` element. We cannot use MutationObserver
// directly on the reference element because it will be removed
// and we need to attach it on an element which will remain in the
// DOM which is their parent `ul`.
const target_node = $(instance.reference).parents("ul").get(0);
// We only need to know if any of the `li` elements were removed.
const config = {attributes: false, childList: true, subtree: false};
const callback = function (mutationsList) {
for (const mutation of mutationsList) {
// Hide instance if reference is in the removed node list.
if (
Array.prototype.includes.call(
mutation.removedNodes,
instance.reference.parentElement,
)
) {
instance.hide();
}
}
};
observer = new MutationObserver(callback);
observer.observe(target_node, config);
},
});
}