Check if Node created with insertAdjacentHTML in a loop is "visible"

Asked

Viewed 30 times

0

I’m looking for fragments of HTML with fetch() and adding to the DOM with the function insertAdjacentHTML() in a loop for()... the function that performs this task is in a Promise() and its return (in case of success) use another function to modify the content of parts of the document that contain a certain attribute.

The following example is true to the question:

let fragments = [
    {
       file: "navbar.html",
       mode: "prepend",
       target: "#body"
    },
    {
       file: "modal-cookies.html",
       mode: "insertAfter",
       target: ".drawer-menu"
    },
    {
       file: "footer.html",
       mode: "append",
       target: "#body"
    }
]

//
const __DEFINE__ = () => {
    return new Promise((resolve, reject) => {

        let urls = [],
            param = [],
            len = fragments.length

        for (let i = 0; i < len; i++) {
             urls.push(fragments[i].file);
             param.push({
                 mode: fragments[i].mode,
                 target: fragments[i].target
             })
        }

        let fetchResource = (url, i) => {
            return fetch(url).then(response => {
                if ( response.ok ) {
                    return response.text()
                }
                return Promise.reject()
            }).then(text => {
                return text
            }).catch(e => {
                return Promise.reject(e)
            })
        }

        Promise.all(urls.map(fetchResource)).then(response => {

            for (let i = 0; i < response.length; i++) {
                 let target = document.querySelector(param[i].target)
                 switch (param[i].mode) {
                     case 'insertBefore':
                        target.insertAdjacentHTML('beforebegin', response[i])
                     break
                     case 'insertAfter':
                        target.insertAdjacentHTML('afterend', response[i])
                     break
                     case 'append':
                        target.insertAdjacentHTML('beforeend', response[i])
                     break
                     case 'prepend':
                        target.insertAdjacentHTML('afterbegin', response[i])
                     break
                 }
            }
            // após processar o loop "resolver"
            resolve()
        }).catch(e => {
            reject(e)
        })
    })
}

//
__DEFINE__().then(() => {
    // manipular o DOM após ter adicionado novos Node's
    let allTargets = document.querySelectorAll('.uma-classe')
    [...allTargets].forEach(item => {
        //
        item.innerHTML = 'Exemplo'
    })
}).catch(e => {
    console.error(e)
})

Apparently the loop was processed and new "Node’s" were added to the DOM but when executing the function that had modified parts of these new elements sometimes by loading the page "it seems" that the "Node’s" did not finish being accommodated to the DOM and consequently the changes are not carried out.

Within Promisse.all() how to check if these "Node’s" have been completely added to the DOM?

1 answer

0

Not long after asking I arrived at a satisfactory logic ... a pity it takes to share the result here, but never too late.

In the bowels of the matter

Initially it was marking classes throughout the document to map them with querySelector and "translate" by following a set of words on an object JSON (a "dictionary").

It made me care JSON (fetch) at the beginning of the script, traverse the document and add the corresponding words based on their respective indexes obtained from an attribute "date-", something like this:

<a href="/contact" data-i18n-html="navbar.contact" class="i18n"></a>

But, some "fragments" of documents were added dynamically (fetch) which required me to cross the document again to redo everything again ... it was costly, logically unnecessary and presenting some errors that I was not able to deal with. The question cites the most serious of these errors.

The alternative

I had to add a property called "Translated="true" for the elements found in the "first round" held while traversing the document and Mutationobserver for iterated items of Promise.all (text) and added to the document (nodes), this allowed me to iterate only on these nodes and all their underlying descendants with the class i18n which (yet) did not contain the property Translated without the need to go through the whole document again several times and ensured that now in fact all these elements are "translated".

The result

The substantial part that changed was exactly this:

// utilidades
const UTILS = {
    objectToString(o) {
        return Object.prototype.toString.call(o)
    },
    type(obj) {
        return (obj) ? UTILS.objectToString(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase() : 'undefined'
    }
}
// MutationObserver
let SpyDOM = (node, callback, options) => {
    let observer = new MutationObserver(callback)
    observer.observe(node, options)
}
// request resource
Promise.all(urls.map(fetchResource)).then(response => {
    response.forEach((item, i, array) => {
        // refuse "undefined"
        if ( item ) {
            let target = documento.querySelector(param[i].target)
            // initialize MutationObserver
            SpyDOM(target, mutations => {
                mutations.forEach(mutation => {
                    ;[...mutation.addedNodes].forEach(addedNode => {
                        if ( /element/g.test(UTILS.type(addedNode)) ) {
                            ;[...addedNode.querySelectorAll('[class^="i18n"]:not([translated])')].forEach(node => {
                                // "traduzir" este Node
                                funcao_traduzir(node)
                            })
                        }
                    })
                })
            }, {childList: true})
            switch (param[i].mode) {
                case 'insertBefore':
                    target.insertAdjacentHTML('beforebegin', item)
                break
                case 'insertAfter':
                    target.insertAdjacentHTML('afterend', item)
                break
                case 'append':
                    target.insertAdjacentHTML('beforeend', item)
                break
                case 'prepend':
                    target.insertAdjacentHTML('afterbegin', item)
                break
            }
        }
        // if last item
        if ( i === array.length -1 ) {
            resolve()
        }
    })
}).catch(e => {
    reject('e')
})

I thought I’d close this question but I leave this answer as a reference who knows how to help someone.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.