hello world...">

Highlight results when searching divs

I have a div setting like this:

<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>

      

And I allow the user to search for the div with:

jQuery(document).ready(function() {
    jQuery("#search").on("keyup click input", function () {
        var val = jQuery(this).val();
        if (val.length) {
            jQuery(".entry").hide().filter(function () {
                return jQuery('.title, .description',this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
            }).show();
        }
        else {
            jQuery(".entry").show();
        }
    });
});

      

Works great , try jsFiddle .

My question is, how can I highlight search terms? For example, if the user is searching test

, I want to wrap the text test

in tags <span>

.

EDIT: Please note that I know how to search / replace text, but I cannot get it to work correctly with my search function.

+3


source to share


3 answers


Optimized solution

After all the problems discussed in the comments and trying to optimize the solution, so there will be no shortage of possible errors, I refactored the code and optimized it:

$(document).ready(function() {
  $("#search").on("keyup click input", function() {
    var val = jQuery(this).val();
    var regExp = new RegExp(val, 'ig');
    var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
    if (val.length) {
      $(".entry").hide().filter(function() {
        var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
        if (val.length > 3) {
          $('.title, .description', this).each(function(k, v) {
            if ($(v).text().match(regExp)) {
              $(v).html($(v).text().replace(regExp, '<span class="highlight">$&</span>'));
            } else {
              $(v).html($(v).text().replace(reg, '$&'));
            }
          });
        } else {
          $('.title, .description', this).each(function(k, v) {
            $(v).html($(v).text().replace(reg, '$&'));
          });
        }
        return found;
      }).show();
    } else {
      $('.title, .description').each(function(k, v) {
        $(v).html($(v).text().replace(reg, '$&'));
      });
      $(".entry").show();
    }
  });
});
      

.highlight {
  background-color: blue
}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>
      

Run codeHide result


It iterates over the elements and uses a RegExp with the appropriate group, and if the content of the iterated element matches the Regex, replace the matched text with the same content enclosed in a range, otherwise just set the content to its original form.


Original Answer

Here's how you should do it:

    var val = jQuery(this).val();
    if (val.length) {
      $(".entry").hide().filter(function() {
        var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
        var regExp = new RegExp(val, 'ig');
        $('.title, .description', this).each(function(k, v) {
          if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
            var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
            $(v).html(newHTML);
          }
        });
        return found;
      }).show();
    } else {
      $(".entry").show();
    }

      



You need to iterate over the elements and use RegExp with the appropriate group, and if the content of that element matches your Regex, replace the matched text with the same content enclosed in a gap.

Demo:

This is a working demo:

$(document).ready(function() {
  $("#search").on("keyup click input", function() {
      var val = jQuery(this).val();
      if (val.length) {
        $(".entry").hide().filter(function() {
            var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
            var regExp = new RegExp(val, 'ig');
              $('.title, .description', this).each(function(k, v) {
                if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
                  var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
                  $(v).html(newHTML);
                }
              });
          return found;
        }).show();
    } else {
      $('.title, .description').each(function(k, v) {
        var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
        var newHTML = $(v).text().replace(reg, '$&');
        $(v).html(newHTML);
      });
      $(".entry").show();
    }
  });
});
      

.highlight {
  background-color: blue
}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>
      

Run codeHide result


Edit:

This is a demo that only highlights sentences when you type more than two letters:

$(document).ready(function() {
  $("#search").on("keyup click input", function() {
    var val = jQuery(this).val();
    if (val.length) {
      $(".entry").hide().filter(function() {
        var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
        var regExp = new RegExp(val, 'ig');
        if (val.length > 2) {
          $('.title, .description', this).each(function(k, v) {
            if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
              var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
              $(v).html(newHTML);
            }
          });
        } else {
          $('.title, .description').each(function(k, v) {
            var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
            var newHTML = $(v).text().replace(reg, '$&');
            $(v).html(newHTML);
          });
        }
        return found;
      }).show();
    } else {
      $('.title, .description').each(function(k, v) {
        var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
        var newHTML = $(v).text().replace(reg, '$&');
        $(v).html(newHTML);
      });
      $(".entry").show();
    }
  });
});
      

.highlight {
  background-color: blue
}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>
      

Run codeHide result


+2


source


Try to hide all divs contains(text)

instead of filter()

.inintile.Then Show only text contains div

. And apply the span element to the corresponding letter in the children usingnew RegExp()

To ignore case matching ig

in regex and also added code for case insensitive forcontains

Updated Fixed with .title, .description

for kids



jQuery(document).ready(function() {
  jQuery("#search").on("input", function() {
    var val = jQuery(this).val()
    jQuery(".entry").hide()
    jQuery(".entry:contains(" + val + ")").show()
    jQuery(".entry").each(function() {
      if ($(this).find(".title, .description:contains(" + val + ")")) {
        $(this).find(".title, .description:contains(" + val + ")").html(function() {
          return $(this).text().replace(new RegExp('('+val+')', 'ig'), '<span>$1</span>')
        })
      }
    })
  });

})
jQuery.expr[':'].contains = function(a, i, m) {
  return jQuery(a).text().toUpperCase()
      .indexOf(m[3].toUpperCase()) >= 0;
};
      

.entry {
  background: #fff;
  margin-bottom: 10px;
  width: 300px;
}

span {
  color: red;
}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">Attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>
<div class="entry">
<div>
  <div class="title">For nested element on titan</div>
  <div>
  <div class="description">fullmetal alchemist nested</div>
  </div>
  </div>
</div>
      

Run codeHide result


+2


source


Try the following:

document.getElementById('search').onkeyup = userInput;
document.getElementById('search').onclick = userInput;
document.getElementById('search').oninput = userInput;
var allEntries = document.querySelectorAll('.entry');

function userInput () {
    var val = this.value;
    for (var i = 0; i < allEntries.length; i++) {
        var entryElement    = allEntries[i];
        var title           = entryElement.querySelector('.title');
        var description     = entryElement.querySelector('.description');
        var noHtmlSearchStr = '';
        if (title)       noHtmlSearchStr +=       title.innerText;
        if (description) noHtmlSearchStr += description.innerText;
        if (noHtmlSearchStr.length > 0) {
            if (noHtmlSearchStr.toLowerCase().indexOf(val.toLowerCase()) != -1) {
                // Remove existing <b> tags.
                var regexp1 = new RegExp('(<b>|<\/b>)', 'gi');
                if (title)             title.innerHTML =       title.innerHTML.replace(regexp1, '');
                if (description) description.innerHTML = description.innerHTML.replace(regexp1, '');

                if (val.length > 3) {
                    var regexp2 = new RegExp('(' + val + ')(?!>)', 'gi');
                    if (title)             title.innerHTML =       title.innerHTML.replace(regexp2, '<b>$1</b>');
                    if (description) description.innerHTML = description.innerHTML.replace(regexp2, '<b>$1</b>');
                }
                entryElement.style.display = 'block';
            } else {
                entryElement.style.display = 'none';
            }
        }
    }
}
      

.entry {
    background: #fff;
    margin-bottom: 10px;
    width: 300px;
}
      

<input id="search">

<div class="entry">
    <div class="title">hello world test 123</div>
    <div class="description">div lorem <span>ipsum</span> test <div>test1</div> testing span</div>
</div>

<div class="entry">
    <div class="title">attack on titan</div>
    <div class="description">fullmetal alchemist</div>
</div>

<div class="entry"></div>

<div class="entry">
    <div class="title">attack on titan</div>
</div>

<div class="entry">
    <div class="description">Let not go to Camelot, 'tis a silly place</div>
</div>
      

Run codeHide result


Explanation of JS code

  • Bind all events to a function userInput()

    .
  • Get all the elements of the class .entry

    and store them in allEntries

    .
  • Get user input and save to val

    .
  • Iterate through allEntries

    .
  • Get search text from title

    and description

    and save to noHtmlSearchStr

    .
  • If it val

    matches some part noHtmlSearchStr

    , then show it entryElement

    , otherwise hide it.
  • Remove tags <b>

    from title

    and description

    .
  • If the user search length ( val

    ) is longer than three characters, select the text matches, otherwise do not select anything.
+2


source







All Articles