2

I need your help.

How can I add build some functionality to my existing code such that I would be able to search in the ul list using an input box and automatically highlight each match like a (find-as-you-type) style kind of search?

I am jQuery friendly :)

For the fiddlers out there here is a fiddle: http://jsfiddle.net/83sPQ/1/

Here is the markup:

<!DOCTYPE html>

<html>

<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> 

<style type="text/css">
* {
    font-family: Segoe UI;
    font-size: 9pt;
}
#refdocs {
    border: 0;
    width: 100%;
    height: auto;
    padding-left: 2px;
}
#refdocs_main {
    border: 1px solid rgb(170,170,170);
    width: 179px;
    overflow: hidden;
    margin-top: 1px;
}
#refdocs_input{
    border-bottom: 1px solid rgb(170,170,170);
    height: 20px;

}
#refdocs_wrapper{
    height: 100px;
    overflow-y: scroll;
    overflow-x: hidden;
}
#refdocs_list {
    width: 100%;
}
#refdocs_list ul {
    margin: 0;
    padding: 0px;
    list-style-type: none;
}
#refdocs_list li {
    cursor: default;
    padding: 2px;
}
.selected {
    background: rgb(228,228,228);
}

</style>

<script type="text/javascript">
window.onload = function() {
    $('#refdocs_list ul li').click(function () {
        $('#refdocs_list ul li').removeClass('selected');
        $(this).addClass('selected');
        document.getElementById('refdocs').value = $(this).text()
    });


}




</script>

</head>

<body>

    <div id="refdocs_main">

        <div id="refdocs_input"><input type="text" id="refdocs"></div>

        <div id="refdocs_wrapper">

            <div id="refdocs_list">
                <ul>
                    <li>9094203</li>
                    <li>9279863</li>
                    <li>9023698</li>
                    <li>8993127</li>
                    <li>9037891</li>
                    <li>(red)</li>
                    <li>tiger</li>
                    <li>The lion</li>
                    <li>Ted</li>
                </ul>
            </div>

        </div>

    </div>

</body>

</html>
Jason Kelly
  • 2,539
  • 10
  • 43
  • 80
  • 1
    Where in your code you actually listen for input `keydown` events? What have you tried to accomplish your task? – Roko C. Buljan Jun 09 '14 at 20:33
  • ...and how many letters/characters should match? – sinisake Jun 09 '14 at 20:34
  • Thanks you, you have an overflow element so you also need someone to tell you how make it scroll (while highlighting)? Or you know that part? please share some more code... your best try at least. – Roko C. Buljan Jun 09 '14 at 20:34
  • 1
    `$('#refdocs').on('keyup', function() { var val = $(this).val(); $('#refdocs_list li').each(function() { $(this).toggle(!!$(this).text().match(val)); }); });` – aebabis Jun 09 '14 at 20:35
  • @acbabis ??? toggle what? – Roko C. Buljan Jun 09 '14 at 20:36
  • @RokoC.Buljan For each `li` test if it matches the value, cast the result to a boolean and pass that to toggle. – aebabis Jun 09 '14 at 20:37
  • why didnt you put that in an answer acbabis??? – Andrew Jun 09 '14 at 20:37
  • @Andrew Because it doesn't do everything the OP asked. I guess I should add that the OP should look at using the `mark` tag – aebabis Jun 09 '14 at 20:39
  • @acbabis I know what it does, just... why Toggle? – Roko C. Buljan Jun 09 '14 at 20:39
  • `$('#refdocs_list ul li')` and than suddenly you use `document.getElementById('refdocs')`? Why not go on with jQuery? – Roko C. Buljan Jun 09 '14 at 20:41
  • @RokoC.Buljan If you mean why use toggle specifically, its because I like the succinctness of jQuery `toggle`. If you mean why hide the elements, its because most searches like this usually hide. – aebabis Jun 09 '14 at 20:41
  • @acbabis the way you're doing it is totally fine (not what the OP wants but...) I really like your approach. Post as an answer. (mention also why you did it like that!) – Roko C. Buljan Jun 09 '14 at 20:43

2 Answers2

3
$('#refdocs').on('keyup change', function () {
    var search = $(this).val();
    $('#refdocs_list li').each(function () {
        var val = $(this).text();
        $(this).toggle( !! val.match(search)).html(
            val.replace(search, function(match) {
                    return '<mark>'+match+'</mark>'}, 'gi')
        );
    });
});

Whenever the user types, the li's are dynamically hidden or shown based on whether they match the regex. Their text is also highlighted using the search text as a regular expression to wrap the matches in mark tags.

http://jsfiddle.net/acbabis/4cfQ8/

EDIT: Per request, here is a more robust solution:

$('#refdocs').on('keyup change', function () {
    var search = $(this).val();
    var searchLen = search.length;
    // Case-insensitive search with regex characters escaped
    // For case-sensitive, no regex is needed. Use indexOf() instead of search().
    // Attr: http://stackoverflow.com/a/3561711/2993478
    var regex = new RegExp(
            search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'i');
    $('#refdocs_list li').each(function () {
        var li = $(this);
        var val = li.text();
        if (searchLen === 0) {
            li.show().text(val); // Remove inner markup
            return;
        }
        var index;
        var isMatch = false;
        li.html('');
        while ((index = val.search(regex)) != -1) {
            isMatch = true;
            if (index !== 0) {
                var nonMatch = val.substring(0, index);
                li.append($('<span>').text(nonMatch));
            }
            var match = val.substring(index, index + searchLen);
            val = val.substring(index + searchLen);
            li.append($('<mark>').text(match));
        }
        if (val.length) {
            li.append($('<span>').text(val));
        }
        li.toggle(isMatch); // Optional. Hide non-matches
    });
});

This code relies on the ability of the jQuery text() function to HTML escape characters.

http://jsfiddle.net/acbabis/3bUxe/

aebabis
  • 3,657
  • 3
  • 22
  • 40
  • I really liked your solution, and was hoping that maybe, you can also make it function for when the user puts in special characters like brackets, exclamation marks, number signs ect. Is there a neat way to have your script modified to include special chracters? – Jason Kelly Jun 10 '14 at 01:07
2

This should be a reasonable solution:

$('#refdocs').keyup(function (event) {
    var text = $(this).val();
    $('#refdocs_list ul li').each(function () {
        var elem = $(this);
        elem.removeClass('selected');
        if (elem.html().indexOf(text) != -1) {
            elem.addClass('selected');
        }
    });
});

JSFiddle: http://jsfiddle.net/83sPQ/4/

Orel Eraki
  • 11,940
  • 3
  • 28
  • 36
  • Good solution. You can even do `if (elem.html().toLowerCase().indexOf(text.toLowerCase()) != -1) {` to make it case insensitive. – agrm Jun 09 '14 at 20:58
  • @agrm, Thanks mate for your comment.. And if needed I'm sure the author will add your improvement as well. – Orel Eraki Jun 09 '14 at 21:01
  • Nice solution, you could add a class `.hided:{display:none;}` and selecting matching and hiding non-matching elements with and `else`to your if. – lisandro Feb 27 '21 at 14:34