How can I filter and mark words from an input without html markup being displayed?

Viewed 125

I am trying to filter and mark word from a webpage, and Sajeeb Ahamed graciously assisted me with a piece of code that functions exactly as I wanted,however when I add and other element tags such as an list item or heading tag when I clear the input box it displays the HTML markup.

$(document).ready(function() {
  $("#myInput").on("keyup", function() {
    var value = $(this).val().toLowerCase();
    $("#myDIV>*").map(function() {
      var el = $(this);
      var content = el.html().replace(/(<span class="highlighted">)|(<\/span>)/g, "");
      el.html(content);
      var hasText = el.text().toLowerCase().indexOf(value) > -1;
      el.toggle(hasText);
      if (hasText) {
        // escape value for use in regex
        var textRegex = new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
        el.html(content.replace(textRegex, '<span class="highlighted">$&</span>'));
      }
    });
  });
});
.highlighted {
  background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<input id="myInput" />
<!-- the new search -->
<div id="myDIV">
  <p>This is a test</p>

  <ul>
    <li>This is a list item</li>
    <li>This is a another list item</li>
  </ul>

  <a href="">This is a link</a>

</div>

This the code, it will only accept a paragraph tag. Does anybody have any idea why?

Thanks

3 Answers

It works with any element inside the element with id 'myDIV' at first level (not deep),because you are using this selector $("#myDIV>*"). Be sure that your tag is inside this rule.

UPDATE WITH NEW INFORMATION

$(document).ready(function() {
  $("#myInput").on("keyup", function() {
    var value = $(this).val().toLowerCase();
    
    // Remove all class="highlighted" inside #myDIV 
    $("#myDIV").html($("#myDIV").html().replace(/(<span class="highlighted">)|(<\/span>)/g, ""))
    
    $("#myDIV *").map(function() {
        var el = $(this);
        // Only in deep children aplly your logic
        if (el.children().length == 0) {
        var content = el.html().replace(/(<span class="highlighted">)|(<\/span>)/g, "");
        el.html(content);
        var hasText = el.text().toLowerCase().indexOf(value) > -1;
        el.toggle(hasText);
        if (hasText) {
          // escape value for use in regex
          var textRegex = new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
          el.html(content.replace(textRegex, '<span class="highlighted">$&</span>'));
        }
      }
    });
  });
});
.highlighted {
  background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<input id="myInput" />
<!-- the new search -->
<div id="myDIV">
  <p>This is a test</p>

  <ul>
    <li>This is a list item</li>
    <li>This is a another list item</li>
  </ul>

  <a href="">This is a link</a>

</div>

You need to apply changes on deep children. and remove class highlight on begining

Most clean approach, rest div and start again. So before the start, I took the snapshot of div and save it. Every time data change I reconstruct it. Easy to understand and scale.

Suggestion: The UI should be data-driven. Not HTML/content-driven. You can create a list of data and construct on every change.

Checkout my second example

Do not mutate state/UI[React]

$(document).ready(function () {
  const div = $("#myDIV").html();
  $("#myInput").on("keyup", function () {
    var value = $(this).val().toLowerCase();
    $("#myDIV").html(div); //Reset
    const p = $("#myDIV p");
    var hasText = p.text().toLowerCase().indexOf(value) > -1;
    hasText && highlight(p, value);
    $("#myDIV li").map(function () {
      var el = $(this);
      var hasText = el.text().toLowerCase().indexOf(value) > -1;
      if (hasText) {
        highlight(el, value);
      } else {
        el.remove();
      }
    });
  });
});
function highlight(elm, text) {
  elm.html(
    elm
      .html()
      .replace(new RegExp(`(${text})`), '<span class="highlighted">$1</span>')
  );
}
.highlighted {
  background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<input id="myInput" />
<!-- the new search -->
<div id="myDIV">
    <p>This is a test</p>
    <ul>
        <li>This is a list item</li>
        <li>This is a another list item</li>
    </ul>
    <a href="">This is a link</a>
</div>

Using data-driven approach.

$(document).ready(function () {
  const list = ["This is a list item", "This is a another list item"];
  function buildUi(keyword) {
    $("#filter .list").html("")
    list.forEach((text) => {
      if (!keyword || text.toLowerCase().indexOf(keyword) !== -1) {
        text = text.replace(
          new RegExp(`(${keyword})`),
          '<span class="highlighted">$1</span>'
        );
      } else {
        return;
      }
      const li = $(`<li>${text}</li>`);
      $("#filter .list").append(li);
    });
  }
  buildUi("");
  $("#myInput").on("keyup", function () {
    const keyword = $(this).val().toLowerCase()
    buildUi(keyword)
  });
});
.highlighted {
  background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<input id="myInput" />
<!-- the new search -->
<div id="filter">
    <p>This is a test</p>
    <ul class="list">
    </ul>
    <a href="">This is a link</a>
</div>

Hi guys so i found what i was looking for, thanks to the assistance of the great guys here and a lot of head banging, this script works for a local webpage search and filter, it must run in conjunction with jsquery mini and the hilitor.js file. This ought to be worth something to somebody out there. Happy coding guys.

<script src="../js/hilitor.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
window.addEventListener("DOMContentLoaded", function(e) {
    var myHilitor2 = new Hilitor("playground");
    myHilitor2.setMatchType("left");
    document.getElementById("keywords").addEventListener("keyup", function(e) {
      myHilitor2.apply(this.value);
    }, false);
  }, false);
$(document).ready(function(){
  $("#keywords").on("keyup", function() {
    var value = $(this).val().toLowerCase();
    $("#playground *").filter(function() {
      $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
    });
  });
});
</script>
<label>Search And Filter</label><input id="keywords" type="text" placeholder="Search And Filter.." onKeyDown="if(event.keyCode === 32) return false;">

<div id="playground" > 
<ul>
 <li>Hey diddle diddle,</li>
<li>The cat and the fiddle,</li>
<li>The cow jumped over the moon.</li>
<li>The little dog laughed to see such sport,</li>
<li>And the dish ran away with the spoon.</li>
 </ul>
</div>

 

Related