flowbite icon indicating copy to clipboard operation
flowbite copied to clipboard

modal open with javascript, not close correctly

Open KinG-InFeT opened this issue 3 years ago • 26 comments

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.

KinG-InFeT avatar Mar 24 '22 11:03 KinG-InFeT

Hey @KinG-InFeT,

If you use the object methods you need to add the event listeners yourself on the close icon to close the same instance of the Modal.

For example:

const modal = new Modal(document.getElementById('defaultModal'), null).show();
const icon = document.getElementById('close-icon');

icon.addEventListener('click', () => {
    modal.hide();
})

zoltanszogyenyi avatar Mar 25 '22 11:03 zoltanszogyenyi

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

KinG-InFeT avatar Mar 25 '22 11:03 KinG-InFeT

@zoltanszogyenyi zoltanszogyenyi the background is: modal-backdrop not removed by javascript

KinG-InFeT avatar Mar 28 '22 13:03 KinG-InFeT

@zoltanszogyenyi i resolve my problem with small javascript code (very very simple) with jquery

$('[data-modal-toggle]').on('click', function (event) {
        var modal_id = $(this).data("modal-toggle");
        if($('#'+modal_id).hasClass("hidden")) {
            $('[modal-backdrop]').remove();
        }
    });

KinG-InFeT avatar Mar 28 '22 13:03 KinG-InFeT

Hi guys.

Coming from bootstrap modals (pure HTML and Vue), this seems hacky. If I initialize the modal via JS, and toggle it via JS, and then click on a button with data-modal-toggle, the background stays on.

modal = new Modal(targetEl, options);
    modal.show();
<button type="button"
          class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
          data-modal-toggle="{{$id}}">
          <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd"
              d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
              clip-rule="evenodd"></path>
          </svg>
        </button>

CleanShot 2022-04-03 at 08 26 39

I ended up using custom data-modal-hide and data-modal-show to make this work. Too bad though the toggle doesn't work :/

michelvermeulen avatar Apr 03 '22 06:04 michelvermeulen

Alright so looking at the sourcecode, it seems like the lib is automatically creating an instance of the modal via the data-modal-toggle attribute, and then manually creating the modal via custom JS creates another instance of the same modal.

Are there any instructions on how to toggle a modal in the JS without re-creating a new modal instance? Could we have the getModalInstance exposed so we can use it to programatically change the modal visibility?

michelvermeulen avatar Apr 03 '22 06:04 michelvermeulen

I am having also the same issue, the modal is created with js and when it is closed through any button the backdrop stays on. Didn't understood @michelvermeulen solution with data-modal-hide and data-modal-show.

mpapado3 avatar Apr 15 '22 08:04 mpapado3

I've just pushed a pull request to add an attribute so modals can be opened by default, without the need for any JS. Fingers crossed it accepted. https://github.com/themesberg/flowbite/pull/141

csoutham avatar Apr 27 '22 13:04 csoutham

@csoutham merged the PR. I think it's safe to close this issue :)

zoltanszogyenyi avatar Apr 28 '22 14:04 zoltanszogyenyi

@csoutham merged the PR. I think it's safe to close this issue :)

hello, the PR however does not solve the problem in the ticket, it only helps not to open the modal via javascript, avoiding the basic problem. I believe the bug will still have to be fixed.

KinG-InFeT avatar Apr 28 '22 15:04 KinG-InFeT

any update?

KinG-InFeT avatar May 13 '22 16:05 KinG-InFeT

Same problem

marekzak avatar May 27 '22 07:05 marekzak

Same problem

maninthemirror avatar May 30 '22 09:05 maninthemirror

Same for me

lurepos avatar Jun 09 '22 13:06 lurepos

Same here

Rodrigo-lpds avatar Jun 13 '22 19:06 Rodrigo-lpds

Same problem here

harrisonratcliffe avatar Jun 28 '22 10:06 harrisonratcliffe

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.

zoltanszogyenyi avatar Jun 28 '22 10:06 zoltanszogyenyi

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!

KinG-InFeT avatar Jun 28 '22 10:06 KinG-InFeT

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.

harrisonratcliffe avatar Jun 28 '22 11:06 harrisonratcliffe

Hi guys.

Coming from bootstrap modals (pure HTML and Vue), this seems hacky. If I initialize the modal via JS, and toggle it via JS, and then click on a button with data-modal-toggle, the background stays on.

modal = new Modal(targetEl, options);
    modal.show();
<button type="button"
          class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
          data-modal-toggle="{{$id}}">
          <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd"
              d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
              clip-rule="evenodd"></path>
          </svg>
        </button>

CleanShot 2022-04-03 at 08 26 39 CleanShot 2022-04-03 at 08 26 39

I ended up using custom data-modal-hide and data-modal-show to make this work. Too bad though the toggle doesn't work :/

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

harrisonratcliffe avatar Jun 28 '22 11:06 harrisonratcliffe

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/

KinG-InFeT avatar Jun 28 '22 15:06 KinG-InFeT

any update ?

esteban0517 avatar Jul 01 '22 09:07 esteban0517

Hey everyone,

I'll get on this for the next update in the next two weeks.

I agree with the general idea so we'll provide a fix for this.

Thanks for your patience!

PS: if anyone wants to contribute they are more than welcome. The code can be found inside the modal.js file.

zoltanszogyenyi avatar Jul 01 '22 12:07 zoltanszogyenyi

new demo with latest css and js version: https://jsfiddle.net/vincenzo_hpo/z9scjduf/ , bug persist with v1.5.1

HPODEV avatar Jul 28 '22 15:07 HPODEV

Hi guys,

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

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

Tyjow avatar Aug 22 '22 13:08 Tyjow

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

marciotoshio avatar Sep 07 '22 02:09 marciotoshio

Still facing this issue. Anyone with a fix without involving JQuery?

yaqshank92 avatar Dec 06 '22 12:12 yaqshank92

Hi guys,

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

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

@yaqshank92 see this comment, use onHide event without jquery

KinG-InFeT avatar Dec 06 '22 12:12 KinG-InFeT

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

zoltanszogyenyi avatar Dec 14 '22 12:12 zoltanszogyenyi

// 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 :D

paul2Dev avatar Dec 19 '22 14:12 paul2Dev