DEV Community

Imam Ali Mustofa
Imam Ali Mustofa

Posted on • Originally published at darkterminal.prose.sh

Reusable Input Datalist

Reusable Input Datalist

Hello Punk! In my daily boring...

I found something interesting that I will share with you, this is not too fancy or glam but still "fun-cy" for me, cz In my stupid knowledge this is really helpful and enough for me and myself.

HTML5 Input Datalist

At least, this is what W3Schools said in their example:

W3Schools Example

What if I wrap inside PHP Function?

When I work with HTMX I need isolated component that can be reusable a form. So I create a PHP Function that generate the Input Datalist.

// Filename: helpers/Utils.php

public static function selectSearch(string $inputId, array $options, int|string $selected = ''): string
{
    $inputHTML = '<div class="relative">';
    $inputHTML .= '<input type="text" id="' . $inputId . '" list="" placeholder="Search..." autocomplete="off" class="transition-[0.2s] w-full input input-sm input-bordered focus:shadow-md" />';
    $inputHTML .= '<datalist id="' . $inputId . '_list" class="box-border overflow-y-auto absolute top-full mt-1 max-h-40 bg-white border shadow-md">';

    $selectedText = '';
    $selectedValue = '';
    foreach ($options as $option) {
        $text = \explode('#', $option)[1];
        $value = \explode('#', $option)[0];
        if ($selected == $value) {
            $selectedText = $text;
            $selectedValue = $value;
        }
        $inputHTML .= '<option class="block p-1 mb-px cursor-pointer hover:bg-gray-200" value="' . $option . '">' . $text . '</option>';
    }

    $inputHTML .= '</datalist>';
    $inputHTML .= '<input type="hidden" name="' . $inputId . '" id="target_' . $inputId . '" value="'.$selectedValue.'" />';
    $inputHTML .= '</div>';
    $inputHTML .= '<script type="text/javascript">';
    $inputHTML .= 'fancyDropdown("' . $inputId . '");';
    $inputHTML .= 'setTimeout(() => document.getElementById("' . $inputId . '").value = "'.$selectedText.'", 350);';
    $inputHTML .= '</script>';

    return $inputHTML;
}

Enter fullscreen mode Exit fullscreen mode

I create a static function inside Utils class so I can use that function everywhere in my project.

Is that using TailwindCSS? Yes, it's too dirty. But fck it! It's work! (Don't blame on me, if you really programmer so read it)

You can see this script:

$inputHTML .= '<script type="text/javascript">';
$inputHTML .= 'fancyDropdown("' . $inputId . '");';
$inputHTML .= 'setTimeout(() => document.getElementById("' . $inputId . '").value = "' . $selectedText . '", 350);';
$inputHTML .= '</script>';
Enter fullscreen mode Exit fullscreen mode

Yes! Again I wrote JavaScript as a string in PHP, why not! And where is the fckin fancyDropdown come from?!

Here is, I found a Question in StackOverflow that asked about

Question in StackOverflow

The Question: https://stackoverflow.com/q/13693482

Yes! Around 11 years ago, and the solution that match with my need is come around

the solution that match with my need

The Solution: https://stackoverflow.com/a/76178626

The updated version of

The updated version

Original Answer: https://stackoverflow.com/a/74786719

Here is this code:

function fancyDropdown(inputId) {
    const input = document.getElementById(inputId);
    const target = document.getElementById('target_' + inputId);
    const datalist = input.nextElementSibling;
    let minWidth = datalist.offsetWidth;

    function outputsize() {
        if (input.offsetWidth < minWidth) {
            datalist.style.minWidth = input.offsetWidth + 'px';
        } else {
            datalist.style.width = input.offsetWidth + 'px';
        }
    }

    new ResizeObserver(outputsize).observe(input);

    input.addEventListener("input", function(e) {
        datalist.style.display = "block";
        const text = input.value.toUpperCase();
        let hide = 1;
        for (let option of datalist.options) {
            if (option.value.toUpperCase().indexOf(text) > -1) {
                option.style.display = "block";
                hide = 0;
            } else {
                option.style.display = "none";
            }
        }
        if (hide) {
            datalist.style.display = "none";
        }
    });

    input.addEventListener("click", function(e) {
        let hide = 1;
        for (let option of datalist.options) {
            if (window.getComputedStyle(option, null).display == "block") hide = 0;
        }

        if (datalist.style.display == "block" || hide == 1) {
            datalist.style.display = "none";
        } else {
            datalist.style.display = "block";
        }
    });

    document.addEventListener("click", function(e) {
        if (e.target.tagName == "OPTION") {
            let inputValue = e.target.value.split('#');
            input.value = inputValue[1];
            target.value = inputValue[0];
        }
        if (e.target.tagName !== "DATALIST" && e.target.tagName !== "INPUT") {
            datalist.style.display = "none";
        }
    });

    datalist.style.display = "none";
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)