flowbite: Allow access to the object instances created via the data attributes API inside JS (usage example problems with the modal component)

Describe the bug if open modal with javascript, i can’t close modal with javascript helper or data-modal-toggle=“”

To Reproduce Steps to reproduce the behavior:

  1. Go to official site: https://flowbite.com/docs/components/modal/#methods
  2. open modal with javascript from chrome console
  3. new Modal(document.getElementById('defaultModal'), null).show();
  4. Close modal with (X) icon inside modal, not close modal!
  5. no javscript error

Expected behavior close correctly modal

Screenshots image

Desktop (please complete the following information):

  • OS: Ubuntu
  • Browser chrome
  • Version 1.4.1

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context Add any other context about the problem here.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 56 (11 by maintainers)

Most upvoted comments

Hey everyone,

When opening the modal with JavaScript, please use the modal.hide(); method by manually adding the event listener to the close button.

The data-modal-toggle will only work when the modal is initialized only via the data attributes.

Here’s an example:

// set the modal menu element
const targetEl = document.getElementById('modalEl');

// options with default values
const options = {
  placement: 'bottom-right',
  backdropClasses: 'bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40',
  onHide: () => {
      console.log('modal is hidden');
  },
  onShow: () => {
      console.log('modal is shown');
  },
  onToggle: () => {
      console.log('modal has been toggled');
  }
};

Create a new Modal object:

/*
* targetEl: required
* options: optional
*/
const modal = new Modal(targetEl, options);

Use these methods to show or hide the modal:

// show the modal
modal.show();

// hide the modal
modal.hide();

So in this case you can do something like:

<button id="close-modal">x</button>

Then do this:

document.getElementById('close-modal').addEventListener('click', function () {
    modal.hide();
})

Read more about the Modal JS usage on Flowbite.

hello, unfortunately we still do not understand the basic problem. use javascript to close the modal we agree, but if you open it with a normal toggle from a button and then you want to manage the closure with javascript, the reference to “modal” is lost (not to mention the possibility of having more modals open at the same time, how do you manage the reference? the problem still has not been solved and the management of events and the library should be improved to give the possibility to manage the modal in both ways (the correct motion in the management of the modal is exactly as bootstrap does https://getbootstrap.com/docs/5.2/components/modal/). in a nutshell, the backdrop must have the reference to the modal in some way, to be able to fully manage it!

Hey everyone!

Flowbite v2.0 fixes this issue by bringing a brand new Instances Manager.

Please read more about it here:

https://flowbite.com/docs/getting-started/javascript/ https://github.com/themesberg/flowbite/releases/tag/v2.0.0

Cheers, Zoltan

Hi guys,

a quick fix for me, so hope it help you too

onHide: () => {
    console.log('modal is hidden');
    document.querySelector('[modal-backdrop]').remove();
},

Hey everyone,

Just want to let you all know that I will provide a solution for this in the upcoming major version together with introducing prefixes - please stand by on this one. Appreciate the patience and if any one of you want to make a PR for this I’ll be more than happy to have a look at it and even collaborate on it.

I’m currently working on the autocomplete component and right after that I’ll prepare for these two new changes.

Cheers, Zoltan

Hi everyone, I’m facing this issue and I’m proposing a fix here https://github.com/themesberg/flowbite/pull/274.

Can you share the code you used to get around this issue please?

simple, this is an example code to get the problem out: https://jsfiddle.net/2jkws153/

Hey everyone, When opening the modal with JavaScript, please use the modal.hide(); method by manually adding the event listener to the close button. The data-modal-toggle will only work when the modal is initialized only via the data attributes. Here’s an example:

// set the modal menu element
const targetEl = document.getElementById('modalEl');

// options with default values
const options = {
  placement: 'bottom-right',
  backdropClasses: 'bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40',
  onHide: () => {
      console.log('modal is hidden');
  },
  onShow: () => {
      console.log('modal is shown');
  },
  onToggle: () => {
      console.log('modal has been toggled');
  }
};

Create a new Modal object:

/*
* targetEl: required
* options: optional
*/
const modal = new Modal(targetEl, options);

Use these methods to show or hide the modal:

// show the modal
modal.show();

// hide the modal
modal.hide();

So in this case you can do something like:

<button id="close-modal">x</button>

Then do this:

document.getElementById('close-modal').addEventListener('click', function () {
    modal.hide();
})

Read more about the Modal JS usage on Flowbite.

hello, unfortunately we still do not understand the basic problem. use javascript to close the modal we agree, but if you open it with a normal toggle from a button and then you want to manage the closure with javascript, the reference to “modal” is lost (not to mention the possibility of having more modals open at the same time, how do you manage the reference? the problem still has not been solved and the management of events and the library should be improved to give the possibility to manage the modal in both ways (the correct motion in the management of the modal is exactly as bootstrap does https://getbootstrap.com/docs/5.2/components/modal/). in a nutshell, the backdrop must have the reference to the modal in some way, to be able to fully manage it!

This exactly.

Hey everyone! Flowbite v2.0 fixes this issue by bringing a brand new Instances Manager. Please read more about it here: https://flowbite.com/docs/getting-started/javascript/ https://github.com/themesberg/flowbite/releases/tag/v2.0.0 Cheers, Zoltan

finaly!, but out new problem, the modal not centered correctlly, here example: https://jsfiddle.net/vincenzo_hpo/r3ncmk6f/

@KinG-InFeT change to “placement”:“center” in the options

I circumvented it as follows.

const close = element.querySelector('button[data-modal-hide]') as HTMLButtonElement
if (close) {
  close.click()
}

@rmelchner This is a good solution for this particular issue. I added one comment but otherwise, I hope your PR is accepted.

I think there are still some underlying issues resulting from side effects of the init functions (initModals, initDrawers, etc.) being called automatically regardless of whether initFlowbite() is called. For example:

  1. The init functions should be made to be idempotent.
  2. The public methods of components (show, hide, etc.) and the private methods they call (_createBackdrop, _destroyBackdropEl, etc.) should be stateful and not duplicate things that have already been done
  3. If instances are going to be tracked at all, its scope shouldn’t be limited to a closure within the init functions. This makes it impossible to determine whether that instance is already being tracked by a previous call.
  4. Access to the object instances created via data attributes should be given. My third point would probably be necessary to facilitate that.

@zoltanszogyenyi thank you - i updated my PR https://github.com/themesberg/flowbite/pull/649

I tested it and it works as expected.

@KinG-InFeT you can check again was easier than expected.

For all the people that need a quick fix:

const options = {
	placement: 'center',
	backdrop: 'dynamic',
	backdropClasses: 'bg-grey_dark bg-opacity-50 fixed inset-0 z-40',
	closable: true,
	onHide: () => {
		console.log('modal is hidden');
		const modalBackdrop = document.querySelector('[modal-backdrop]');
		if (modalBackdrop) {
			modalBackdrop.classList.add('hide');
		}
	},
	onShow: () => {
		console.log('modal is shown');
	},
	onToggle: () => {
		console.log('modal has been toggled');
	}
}

The important part is onHide.

Any updates to allowing access to the instances that are being created via the data attributes? Thanks!

// show the modal modal.show();

creating a modal object with options and using modal.show() and modal.hide() instead of the default data-modal-toggle, worked for me, tnks 😄

Hey everyone,

I know that this has been long overdue, but with the v1.6.0 update and introducing TypeScript I have finally addressed this issue by changing the data-modal-toggle to data-modal-target which will be initialised only once, and the you can use data-modal-toggle/show/hide={modalId} to search for the modal instance and toggle, show or hide it.

Here’s a breakdown of the code that does this:

const getModalInstance = (id: string, instances: ModalInstance[]) => {
    if (instances.some((modalInstance) => modalInstance.id === id)) {
        return instances.find((modalInstance) => modalInstance.id === id);
    }
    return null;
};

export function initModals() {
    const modalInstances = [] as ModalInstance[];

    document.querySelectorAll('[data-modal-target]').forEach(($triggerEl) => {
        const modalId = $triggerEl.getAttribute('data-modal-toggle');
        const $modalEl = document.getElementById(modalId);
        const placement = $modalEl.getAttribute('data-modal-placement');
        const backdrop = $modalEl.getAttribute('data-modal-backdrop');

        if (!getModalInstance(modalId, modalInstances)) {
            modalInstances.push({
                id: modalId,
                object: new Modal(
                    $modalEl as HTMLElement,
                    {
                        placement: placement ? placement : Default.placement,
                        backdrop: backdrop ? backdrop : Default.backdrop,
                    } as ModalOptions
                ),
            });
        }
    });

    document.querySelectorAll('[data-modal-toggle]').forEach(($triggerEl) => {
        const $targetEl = document.getElementById(
            $triggerEl.getAttribute('data-modal-toggle')
        );
        const modalId = $targetEl.id;
        const modal: ModalInstance = getModalInstance(modalId, modalInstances);

        if (modal) {
            $triggerEl.addEventListener('click', () => {
                modal.object.toggle();
            });
        }
    });

    document.querySelectorAll('[data-modal-show]').forEach(($triggerEl) => {
        const $targetEl = document.getElementById(
            $triggerEl.getAttribute('data-modal-show')
        );
        const modalId = $targetEl.id;
        const modal: ModalInstance = getModalInstance(modalId, modalInstances);

        if (modal) {
            $triggerEl.addEventListener('click', () => {
                if (modal.object.isHidden) {
                    modal.object.show();
                }
            });
        }
    });

    document.querySelectorAll('[data-modal-hide]').forEach(($triggerEl) => {
        const $targetEl = document.getElementById(
            $triggerEl.getAttribute('data-modal-hide')
        );
        const modalId = $targetEl.id;
        const modal: ModalInstance = getModalInstance(modalId, modalInstances);

        if (modal) {
            $triggerEl.addEventListener('click', () => {
                if (modal.object.isVisible) {
                    modal.object.hide();
                }
            });
        }
    });
}

The update will arrive shortly and unfortunately we will have some breaking changes because data-modal-toggle will no longer initialise a modal object, only data-modal-target. The code will not break though.

This also fixed stacking up the event listeners for when click outside of the modal + escape button.

The functionality can be tested on this branch: https://github.com/themesberg/flowbite/tree/1.6.0

@csoutham merged the PR. I think it’s safe to close this issue 😃

hi @zoltanszogyenyi , thank you for reply, after hide, the background continue to appair