Topic: Handle dynamically added HTML

baasjedave premium asked 4 months ago


Hi,

I'm wondering how I should handle dynamically added HTML. For example:On my initially loaded page I don't have a collapse component, so there's no element with the data-mdb-collapse-init attribute. When I add dynamically HTML (either using document.createElement or Fetch API) which does contains an element with the data-mdb-collapse-init it doesn't work (isn't initialized). But, when the initially page does contain a collapse component, the component that comes from dynamically added HTML will do get initialized. The same goes for the data-mdb-ripple-init and possibly other components also.

So it seems like MDB is only observing the components that are present on the initially loaded page. So how should I handle this?

The only way I get it managed is by adding a custom Stimulus Controller to the body of my page. The Stimulus Controller instantiates a MutationObserver to detect newly added nodes and instantiate MDB Components based on the presence of the data-mdb-*-init attribute.

<!-- START: dynamic elements only work when below HTML is initially present -->
<button class="btn btn-primary mb-3" type="button" data-mdb-collapse-init data-mdb-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample">
    Button
</button>
<div class="collapse" id="collapseExample">Lore Ipsum</div>
<!-- END -->

<button onclick="createUsingJS()">Create using JS</button>
<button onclick="createUsingXhr()">Create using XHR</button>

<div id="container"></div>

<script>
    async function createUsingXhr() {
        const response = await fetch('/collapse.html');
        const html = await response.text();
        document.getElementById('container').innerHTML = html;
    }

    function createUsingJS() {
        const btn = document.createElement('button');
        btn.setAttribute('class', 'btn btn-primary mb-3');
        btn.setAttribute('type', 'button');
        btn.setAttribute('data-mdb-collapse-init', '');

        btn.setAttribute('data-mdb-target', '#collapseExample2');
        btn.setAttribute('aria-expanded', 'false');
        btn.setAttribute('aria-controls', 'collapseExample2');
        btn.innerHTML = 'Foo'

        const div = document.createElement('div');
        div.setAttribute('class', 'collapse');
        div.setAttribute('id', 'collapseExample2');
        div.innerHTML = 'Bar';

        const container = document.getElementById('container');
        container.appendChild(btn);
        container.appendChild(div);
    }    
</script>

baasjedave premium answered 4 months ago


'Therefore, for elements added dynamically, we recommend doing init manually. In the createUsingJS function, just add const instance = mdb.Collapse.getOrCreateInstance(btn) at the very end of the function.'

That's contradictory the docs, right? Because in the example of the docs the element that needs to collapse is passed to the constructor.

const element = document.querySelector('.collapse')
const instance = new Collapse(element)

Grzegorz Bujański staff commented 4 months ago

Init with getOrCreateInstance is one of the options for initing a component. The difference is that it checks whether the component requires init or whether it has already been done. As we write in the documentation: Static method which allows you to get the collapse instance associated with a DOM element or create a new one in case it wasn't initialized.


baasjedave premium commented 4 months ago

Hi Grzegorz, thanks for your reaction. I don't mean the difference in constructing (new vd getOrCreateInstance), but the difference in the element that goes into both methods.

Earlier you said I need to call getOrCreateInstance to the button. In the docs the element that goes into the constructor is not the button, but the element that needs to be collapsed. So I'm wondering if I need to pass the button or the collapsable element to the getOrCreateInstance method.


Grzegorz Bujański staff commented 4 months ago

I'm sorry my mistake. In both cases a collapsable element should be passed


baasjedave premium commented 2 months ago

I'm reading the docs about accordion, which is using Collapse under the hood: https://mdbootstrap.com/docs/standard/components/accordion/#docsTabsAPI

In the 'via javascript' example the button gets passed into the Collapse constructor? Now I'm really confused, because one time the button gets passed and the other time the element that needs to be collapsed get passed. Are the docs not up-to-date, is it a bug or am I missing something?


Grzegorz Bujański staff commented 2 months ago

that's a mistake in the documentation. We'll fix it as soon as possible


Grzegorz Bujański staff answered 4 months ago


MDB components are usually automatically initialized if they are available in the dom tree when the page is loaded. In some cases, such as ripple or collapse, an event is additionally attached to the document, which, when clicked, checks whether this element has received init and executes it if necessary. Therefore, as you noticed, if ripple and collapse were added earlier on the page, ripple and collapse added dynamically will also get init.

Therefore, for elements added dynamically, we recommend doing init manually. In the createUsingJS function, just add const instance = mdb.Collapse.getOrCreateInstance(btn) at the very end of the function.

In the createUsingXhr function you must first catch the element with, for example, the querySelector and pass it to the method getOrCreateInstance



Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue

  • ForumUser: Premium
  • Premium support: Yes
  • Technology: MDB Standard
  • MDB Version: MDB5 7.3.0
  • Device: Desktop
  • Browser: Chrome
  • OS: Windows
  • Provided sample code: No
  • Provided link: No