TarekRaafat / autoComplete.js

Simple autocomplete pure vanilla Javascript library.
https://tarekraafat.github.io/autoComplete.js
Apache License 2.0
3.95k stars 240 forks source link

Element added in resultsList.container is removed on second input #206

Closed folknor closed 3 years ago

folknor commented 3 years ago

Here's the code for one of my autocompletes (work in progress)

        new autoComplete({
            data: {
                src: async () => fetchBedriftsInfo(v("bedrift")),
                key: ["navn", "organisasjonsnummer"],
                cache: false,
                results: list => {
                    const q = v("bedrift").toLowerCase()
                    return list.sort((first, second) => {
                        let fs = diceCoefficient(
                            q,
                            first.value.navn.toLowerCase()
                        )
                        let ss = diceCoefficient(
                            q,
                            second.value.navn.toLowerCase()
                        )
                        return ss > fs
                    })
                }
            },
            query: {
                manipulate: query => {
                    let nr = /^\d+$/.test(query.replace(/\s/g, ""))
                    if (nr === true) {
                        return query.replace(/\s/g, "")
                    }
                    return query
                }
            },
            selector: "#bedrift",
            threshold: 3,
            debounce: 300,
            resultItem: {
                content: (data, element) => {
                    if (
                        data.value.forretningsadresse instanceof Object &&
                        typeof data.value.forretningsadresse.poststed ===
                            "string"
                    ) {
                        element.innerHTML = `<span>${data.value.navn}</span><span class="text-muted">${data.value.forretningsadresse.poststed}</span>`
                    } else if (
                        data.value.postadresse instanceof Object &&
                        typeof data.value.postadresse.poststed === "string"
                    ) {
                        element.innerHTML = `<span>${data.value.navn}</span><span class="text-muted">${data.value.postadresse.poststed}</span>`
                    } else {
                        element.innerHTML = `<span>${data.value.navn}</span>`
                    }
                }
            },
            resultsList: {
                container: (element) => {
                    const result = document.createElement("p")
                    result.setAttribute("class", "no_result")
                    result.setAttribute("tabindex", "1")
                    result.innerHTML = `Skriv inn hele organisasjonsnummeret, uten mellomrom.`
                    element.appendChild(result)
                },
                maxResults: 8,
                idName: "bedrift-liste",
                noResults: (list, query) => {
                    const result = document.createElement("li")
                    result.setAttribute("class", "no_result")
                    result.setAttribute("tabindex", "1")
                    let nr = /^\d+$/.test(query)
                    if (nr === true) {
                        result.innerHTML = `Skriv inn hele organisasjonsnummeret, uten mellomrom.`
                    } else {
                        result.innerHTML = `Fant ingen resultat for "${query}".`
                    }
                    list.appendChild(result)
                }
            },
            onSelection: feedback => {
                settBedrift(feedback.selection.value)
                setTimeout(() => {
                    $("epost").focus()
                }, 100)
            }
        })

In resultsList.container I add a p element at the top of the list that I want to use to give helpful information to the user. Here's a screenshot: dropdown (the text in the p is currently wrong, also the element is not styled properly - I just started implementing it, but that's not relevant to the bug)

I think the "problem" is in your function generateList you do list.innerHTML = ""; if list is non-null.

Perhaps it would be possible to add an attribute to my element that makes autoComplete.js not nuke it?

Or maybe you have an alternative solution for me :-) Thank you!

folknor commented 3 years ago

For example a new property on resultsList like append and prepend where we can return HTMLElement that is always inserted before or after the result elements.

Would probably need to have config for if to add them when results are 0. Would be nice to have that information in resultsList.container as well.

TarekRaafat commented 3 years ago

Hello @folknor,

Good catch!

The issue has been fixed in v9.0.4

Check it and let me know how it goes.

Have a nice day! :)

folknor commented 3 years ago

Thank you it works great! :+1:

I changed my code to also make sure clicking the node doesn't close the list like this

container: element => {
    const result = document.createElement("p")
    result.setAttribute("class", "no_result")
    result.setAttribute("tabindex", "1")
    result.innerHTML = `Skriv inn hele organisasjonsnummeret, uten mellomrom.`
    result.addEventListener("click", (ev) => { ev.stopPropagation() } )
    element.appendChild(result)
},
TarekRaafat commented 3 years ago

Glad to hear that! :)

For example a new property on resultsList like append and prepend where we can return HTMLElement that is always inserted before or after the result elements.

Would probably need to have config for if to add them when results are 0. Would be nice to have that information in resultsList.container as well.

Since the element parameter contains the entire list element you can check it if empty to make conditional changes

folknor commented 3 years ago

Since the element parameter contains the entire list element you can check it if empty to make conditional changes

Can you explain how? Because you invoke resultsList.container before adding the child elements at https://github.com/TarekRaafat/autoComplete.js/blob/master/dist/js/autoComplete.js#L204

I think I am misunderstanding something.

folknor commented 3 years ago

Wait you just changed it :-D

TarekRaafat commented 3 years ago

You're right I've just noticed that after my reply and I've just changed it in v9.0.5

Check it and let me know :)

TarekRaafat commented 3 years ago

Wait you just changed it :-D

Yes, my bad 😅

folknor commented 3 years ago

Great work thanks again! :100: :+1:

helpworks noresults

resultsList: {
    container: element => {
        if (element.children.length === 1 && element.children[0].classList.contains("autoComplete_ingen-resultat")) {
            return
        }
        const result = document.createElement("p")
        result.setAttribute("class", "autoComplete_info")
        result.setAttribute("tabindex", "-1")
        result.innerHTML = `<span class="iconify-inline" data-icon="fa-solid:exclamation-triangle"></span>Du må velge en bedrift fra denne listen. Det er ikke nok å skrive inn navnet eller organisasjonsnummeret.`
        result.addEventListener("click", ev => {
            ev.stopPropagation()
        })
        element.prepend(result)
    },
    maxResults: 8,
    idName: "bedrift-liste",
    noResults: (list, query) => {
        const result = document.createElement("li")
        result.setAttribute("class", "autoComplete_ingen-resultat")
        let nr = /^\d+$/.test(query)
        if (nr === true) {
            result.innerHTML = `<span class="iconify-inline" data-icon="fa-solid:exclamation-triangle"></span>Skriv inn hele organisasjonsnummeret, uten mellomrom.`
        } else {
            result.innerHTML = `<span class="iconify-inline" data-icon="fa-solid:exclamation-triangle"></span>Fant ingen resultat for "${query}".`
        }
        list.appendChild(result)
    }
},