Don't write to .innerHTML more than you need to. It causes the DOM to update and you should only do that when you have to. Instead, build up a string and then inject the string once it's complete. In your case, you are writing "No Results" if one particular iteration of the loop doesn't find a match, but other iterations might, so only write "no results" after you know there weren't any. You can use a simple flag variable to keep track of whether matches were found.
Clear the results as you write new data and know that you can't return from a .forEach()
Also, if you are going to convert one string to lower case, you need to also convert the other so that you are comparing lower to lower.
And, change keyup to input so that it will work on devices that don't support keyboard events (i.e. mobile devices), .
var arr = [{name: "Jack"}, {name: "Jake"}];
var txtName = document.getElementById("txtName");
txtName.addEventListener('input', function(e){
myList.innerHTML = ""; // Clear out old results
var output = ""; // This will be the results
var foundItems = false; // "Flag" that keeps track of whether matches were found
// You can't return from a .forEach loop
arr.forEach(function(item){
// Compare lower case against lower case
if(item.name.toLowerCase().indexOf(txtName.value.toLowerCase()) > -1){
output += `<li>${item.name}</li>`
foundItems = true; // Set flag
}
});
// Check flag
if(foundItems){
myList.innerHTML = output; // Flag is up, use found data
} else {
// Flag is still down, no results
myList.innerHTML = `<li>No results </li>`;
}
});
<input type="text" id="txtName">
<ul id="myList"></ul>