Problems with plugin inside reusable, 2 reusables on page

While testing my Tiptap plugin I saw that actions and events were leaking from one to another.

The problem are those floating menus. The way the plugin works is that you create a group in the Bubble editor with your menu, add an ID to that group, and then give that ID to the plugin.

When the plugin is standalone, that’s not a problem. There’s only one div in the DOM with that id.

However, when you create a reusable with the menus inside of it, you end up with multiple divs with the same id.

One option is to build the menu inside the plugin but I’m not keen on this route. I would like to give the users the freedom to style it in Bubble.

How else can I solve this?

the reusable needs to set an unique id. You can have a text state in the reusable that will store your unique id, assign the id from the page that is using the reusable, and use the state for the element id.

1 Like

You need to narrow search scope when looking for an element wtih specified ID. This implies that another setting, e.g. “Scope”, is needed with possible options “Global”, “Siblings”, “Children” (these I ended up with in slider plugin).

And here’s the lookup code:

if ("CHILDREN" === containerLookupScope) {
    element = (o => o.length && o[0] || null)(instance.canvas.parent().find("#" + elementId));
} else if ("SIBLINGS" === containerLookupScope) {
    element = (o => o.length && o[0] || null)(instance.canvas.siblings("#" + elementId));
} else {
    element = document.getElementById(elementId);

There might be some syntax mistakes, I write it by memory, but you can get the idea.
Once you use the plugin inside reusable, narrow Scope to either siblings or children, so that the plugin will only search for target element inside the container element it resides in.

1 Like

Excellent!! That’s a great idea; I’ll give this a try.

I tried this this morning but there was an issue (probably because I was trying to make the ID dynamic).

It is not guaranteed that the dynamic id is already assigned to the element when your plugin’s init or update execute. You have 2 solutions: look for the element and if not present create an observer to get notified when the id gets added, add the dynamic value of the id as a property of your plugin and in your update function check if it has a value.

I know almost everyone using bubble doesn’t care, but having multiple elements with the same id in a web page is bad practice, an id must be unique in the whole document.


Yeah, I see your point. I will implement the above solution for the ease of use. I will also try to fine-tune my implementation with the dynamic ID so that the ID’s are unique.

FYI, I poked in @vladimir.pak 's plugin – very nice plugin, by the way – and I think I see the code you’re referring to:

let element = null;
if (containerId) {
    if ("CHILDREN" === containerLookupScope) {
        const parents = instance.canvas && instance.canvas.parents();
        element = parents && parents.length && $(parents[0]).find("#" + containerId);
        element = element && element.length ? element[0] : null;
    } else if ("SIBLINGS" === containerLookupScope) {
        element = instance.canvas && instance.canvas.siblings("#" + containerId);
        element = element && element.length ? element[0] : null;
    } else {
        element = document.getElementById(containerId);

Yeap, that’s it. Though most of the checks there ain’t necessary for you, it’s an internal stuff.

I got it working, it’s grabbing the right element.

The menu is responding to the right reusable, but clicking on the menu is not working… on to the next adventure.

FYI, I settled on this function:
(couldn’t you use something like this for your plugin?) = function (elementID) {
        let $parent = instance.canvas.parent();
        while ($parent.length > 0) {
            var $foundMenu = $parent.find('#' + elementID);

            if ($foundMenu.length > 0) {
                console.log("found it:", $foundMenu[0])
                return $foundMenu[0];

            $parent = $parent.parent();


Smart approach for your case :+1: