Optimize javascript/jQuery for better performance - javascript

I make Chrome extension for removing li elements from specific UL.
for example i have
<ul>
<li id="1"><div class="l1"><p>test1</p></div></li>
<li id="2"><div class="l1"><p>test2</p></div></li>
<li id="3"><div class="l1"><p>test3</p></div></li>
.... and so on
</ul>
I have array of 3500 items(like database) which on every page reload checks this ul for matches and removes them.
my jQuery code is like this:
var remove = function remove(items){
$.each(items, function(index,value){
if( $("div.l1 p").filter(function() { return $(this).text() == value; }).length ) // if there is match {
$("div.l1 p").filter(function(){ return $(this).text() == value; }).parent().parent().remove(); // delete li element
}
}
}
and i call it like:
var initi = function initi() // called when page is loaded
{
remove(Array("test1","test2","test3",...,"testonemilion"));
}
my problem which happens is sometimes chrome randomly runs out of memory and requires reloading the page. I think this will happen more often when "database" becomes bigger.
so i wanted to ask are there possible ways to optimize my jQuery function, to lower that memory stress?
i use latest chrome 40.0.2214.115 m and have 8GB RAM on my computer(which never gets to 100%)
Thanks!
EDIT: Also are there debugging tools which will display how different versions of function will perform(for example function1() executes for 1 sec, function2() executes for 0.8sec and so on)

You could use filter() to see if text is in an array and therefore only loop through the elements once instead of many times as you are doing
var remove = function remove(items){
$("div.l1 p").filter(function(){
return $.inArray( $(this).text(), items) !==-1;
}).parent().parent().remove();
}
DEMO

You are running 2 filters and then accessing the parent's twice of each filter.
Is it possible that you could do:
<ul>
<li id="1" class="test1"><div class="l1"><p>test1</p></div></li>
<li id="2" class="test2"><div class="l1"><p>test2</p></div></li>
<li id="3" class="test3"><div class="l1"><p>test3</p></div></li>
.... and so on
</ul>
OR
<ul>
<li id="1" data-text="test1"><div class="l1"><p>test1</p></div></li>
<li id="2" data-text="test2"><div class="l1"><p>test2</p></div></li>
<li id="3" data-text="test3"><div class="l1"><p>test3</p></div></li>
.... and so on
</ul>
This way you could use simple selectors on the LIs that you actually want to remove?
var remove = function remove(items){
$.each(items, function(index,value){
$('.'+value).remove();
});
}

Your code can be made more efficient. Since filter() will return an object with the matching elements, there is no need for your if statement.
You should also keep your jQuery object by in a variable, to save having to select all your paragraphs for every iteration of your .each() method.
var remove = function remove(items){
var $p = $("div.l1 p");
$.each(items, function(index,value){
$p.filter(function(){
return $(this).text() == value;
}).parent().parent().remove();
}
}
}
Having said that, this won't make things much quicker for you; 3500+ elements is a lot of elements to loop through by any measure. Paginate your results.

Related

How to Iterate through an Unordered List to get a console.log of the innerText of the LI element

I need to create an event-listener which fires when a user clicks one of the list items in the HTML.The action should call a function named listItemText which returns the innerText of the list item which was clicked--ie: if they click the first li it should log "Walk the dog"
I've tried everything I can think of to get the correct corresponding innerText of the li item that is being clicked on. At best, I've gotten either the whole list back in the console.log, or the last element of the list.
I've tried so many things at this point it would be impossible to recall. More or less the code below is a variant of what I've attempted
<ul id="todo-app">
<li class="item">Walk the dog</li>
<li class="item">Pay bills</li>
<li class="item">Make dinner</li>
<li class="item">Code for one hour</li>
</ul>
var targetUl = document.getElementById('todo-app').childNodes;
this.addEventListener('click', listItemText);
function listItemText(event) {
var liClicked = event.target;
for (i=0; i<targetUl.length; i++) {
if (liClicked == 'LI') {
console.log(targetUl[i].innerText)
}
}
}
I expect to get the text content of the li tag but I keep getting undefined at this point. Help would be greatly appreciated.
If I understand correctly you want the console.log of the text of the li element you click, so I suppose you can try this code below:
var targetUl = document.getElementById('todo-app').addEventListener("click",listItemText);
function listItemText(event) {
var liClicked = event.target;
if(liClicked && liClicked.nodeName == "LI"){
console.log(liClicked.textContent)
}
}
<ul id="todo-app">
<li class="item">Walk the dog</li>
<li class="item">Pay bills</li>
<li class="item">Make dinner</li>
<li class="item">Code for one hour</li>
</ul>
This is the principle of event delegation, where you don't need to attach event listeners on all of the elements but only to the parent node, if the event happened it will bubble up to the parent and using the event.target you can get the reference of the child element which was clicked.
I just now ended up figuring it out. Did it slightly differently, but ultimately the same idea. Was having a hard time understanding event.target and how it works. Here's my answer--which is almost the same as your answer, good sir:
var toDoList = document.getElementById("todo-app");
toDoList.addEventListener('click', listItemText);
function listItemText(e) {
console.log(e.target.innerText);
return e.target.innerText;
}

Filtering through an array to match the text (jQuery / Javascript)

I have an unordered list called $myList. Every time a string ($entry) is entered into an input, I want to run through the list to see if that string is already in the list. If it is, I want to remove the list item that contains the matching string. Either way, the new string gets added and becomes a new list item.
Here's the most recent thing I tried:
$myList.text().filter($entry).remove();
$myList.append($entry);
It doesn't like that I'm doing .text().filter(), but none of the other things I've tried have worked either.
What's a simple way to accomplish this?
The filter method should act on the list items, not on the text() value, since it is one of the list items you might need to remove. The filter method needs a function as argument. That function should determine whether or not the text() value of the iterated item is the entry text. Then the remove will work:
$('li', $myList).filter(function () {
return $(this).text() == entry;
}).remove();
$('button').click(function () {
var entry = $('input').val();
var $myList = $('ul');
$('li', $myList).filter(function () {
return $(this).text() == entry;
}).remove();
$('<li>').text(entry).appendTo($myList); // add item at end of list
$('input').val(''); // clear input
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input><button>Add</button>
<ul></ul>
$myList.find("li").filter(function() { // for each list item
return $(this).text() === $entry; // pick those who have the text equal to $entry
}).remove(); // remove them
$("<li></li>").text($entry) // create a new list item
.appendTo($myList); // add it to $myList
I used the .each function; it iterates through all the items inside the selector. I didn't use .filter, because I though .each would provide a more intuitive programming experience, as you both want to check if something exists and remove it if it does and append that same item to another list.
$myList = $("#myList");
$entry = $("#entry");
$newList = $("#newList");
$entry.on('input', function () {
entryText = $entry.val();
$myList.find("li").each(function () {
if ($(this).html() == entryText) {
$("<li>")
.append($(this).html())
.appendTo($newList);
$(this).remove();
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Let's filter some tags<br />
<ul id="myList">
<li>css</li>
<li>js</li>
<li>so</li>
<li>sass</li>
<li>haml</li>
</ul>
<input type="text" id="entry" />
<ul id="newList">
</ul>

Refreshing the order of an ordered list, after appending a list item

jsFiddle example
I'm attempting to use an ol to display several, dynamic, list items. Upon appending a list item using jQuery, I'd have hoped that the browser would've refreshed the ordered list, however it doesn't appear to be.
<ol>
<li value="1">First</li>
<li value="2">Second</li>
<li value="4">Forth</li>
</ol>
After appending <li value="3">Third</li>, the resulting order is:
// First
// Second
// Forth
// Third <-
Do I need to trigger a re-order, or is the order only calculated upon rendering the page / adding the list DOM element?
Note: I'm using Chrome 28.0.1500.95m
Firstly, note that value is not a valid attribute of li, so your code is invalid. You should use a data attribute for any non-standard attributes you require, the you need to write a sorter function. Try this:
$('#addThird').one('click', function() {
$('ol').append('<li data-value="3">Third</li>');
sortItems();
});
var sortItems = function() {
var $items = $('ol li');
$items.sort(function(a, b) {
var keyA = $(a).data('value');
var keyB = $(b).data('value');
return (keyA > keyB) ? 1 : 0;
});
$.each($items, function(index, row){
$('ol').append(row);
});
}
Updated fiddle
This sorts by the data-value attribute of the element, but that can be amended as required.

Use jQuery to find list items with matching class names in separate unordered lists

I have two unordered lists, each filled with list items that have a DYNAMIC class name. When I say "dynamic" I mean they are not generated by me, but they don't change once the lists have been created. These class names are id's I'm getting from an API, so they're just random numbers. A simple example would be something like...
<ul class="listA">
<li class="123"></li>
<li class="456"></li>
<li class="789"></li>
</ul>
<ul class="listB">
<li class="789"></li>
<li class="101"></li>
<li class="112"></li>
</ul>
What I'm trying to do is compare the two lists, and have any matches be highlighted, in this case the items with the class "789" would match. When I say highlighted, I just mean I'll probably apply some css after a match is found, like maybe a background color or something (not too important yet). The problem really lies in the fact that the lists can be somewhat long (maybe 50 items) and the classes are just random numbers I don't choose, so I can't do any specific searches. Also, there will most likely be cases with multiple matches, or no matches at all.
I'm pretty new to jQuery, so there may be a fairly simple answer, but everything I find online refers to searching by a specific class, such as the .find() method. If anyone needs more info or a better example, I'll be happy to give more info, I'm just trying to keep it simple now.
Thanks so much in advance!
var $first = $('ul.listA li'),
$second = $('ul.listB li');
$first.each(function(){
var cls = this.className,
$m = $second.filter(function(){
return this.className === cls;
});
if ($m.length) {
$(this).add($m).addClass('matched');
}
});
http://jsfiddle.net/b4vFn/
Try it this way:
$("ul.listA li").each(function(){
var listAval = $(this).attr('class');
$("ul.listB li").each(function(){
if(listAval == $(this).attr('class')){
//matched..
return false; //exit loop..
}
}
}
you can find the code here: jsFiddle
var listA=$('.listA li')
var listB=$('.listB li')
listA.each(function(){
var classInA=$(this).attr('class');
listB.each(function(){
var classInB=$(this).attr('class');
if(classInA === classInB){
console.log(classInA);
//now you found the same one
}
})
})
Demo at http://jsfiddle.net/habo/kupd3/
highlightDups();
function highlightDups(){
var classes = [] ;
$('ul[class^="list"]').each(function(k,v){
//alert(v.innerHTML);
$($(this).children()).each(function(nK,nV){
// alert($(this).attr("class"));
classes.push($(this).attr("class"));
});
});
hasDuplicate(classes);
}
//Find duplicate picked from fastest way to detect if duplicate entry exists in javascript array?
function hasDuplicate(arr) {
var i = arr.length, j, val;
while (i--) {
val = arr[i];
j = i;
while (j--) {
if (arr[j] === val) {
// you can write your code here to handle when you find a match
$("."+val).text("This is Duplicate").addClass("match");
}
}
}
}
A slightly less verbose variant of Nix's answer:
$("ul.listA li").each(function(){
var a = $("ul.listB li").filter("." + $(this).attr('class'));
if (a.size()) {
a.add($(this)).css("background", "red");
}
});

jQuery Remove LI from UL with a hyperlink in the LI

I have a unordered list:
<ul id="sortable">
<li id="1" class="ui-state-default">First x</li>
<li id="2" class="ui-state-default">Second x</li>
<li id="3" class="ui-state-default">Third x</li>
</ul>
I want to remove the <li> from the <ul>. I have handled the click event of the class itemDelete where I try to do a remove but I assume its not working because I can't remove the <li> as a child is calling it?
$('.itemDelete').live("click", function() {
var id = $(this).parent().get(0).id;
$("#" + id).remove();
});
What's the best approach?
Assuming you're using a recent version of jQuery:
$('#sortable').on('click', '.itemDelete', function() {
$(this).closest('li').remove();
});
closest is a little more dynamic than parent (although parent works here as well.) It gets the li that is closest to the current element, upwards in the structure.
Actually, the way you have it as of now, id is going to be undefined, because none of the li's have ids.
why not just do
$(this).parent().remove()
also, don't forget to return false.
You don't have IDs on your <li>s
How about simply
$(this).parent().remove();
What wound up working for me:
Prefix your id attributes with a string or underscore (as others have pointed out)
Since frameworks like jQuery Mobile require that ids be unique across all pages (not just in one page, I prefix with the page name, an underscore, and the numerical id that lets me access records in a database.
Instead of binding to a class or the ul control, use 'on' to bind to the li of the parent list:
$('#sortable').on('dblclick', 'li' function() {
aval = $(this).attr('id').match(/\d+/g); // only want the numbers...
id = aval[0];
name = $(this).text(); // in case you need these for a post...
li = $(this); // so we can remove it from the list after the 'this' object changes inside the ajax call...
// make an ajax call to the server
var jqxhr = $.post( "somepage.php", {name: name, id: id},
function(data) {
li.remove();
$("#sortable").listview("refresh");
},'json').fail(function() { alert("error"); });
return false; // preventDefault doesn't seem to work as well...
});
It could also be looking for the index of the elements with event
$('#sortable').on('click', '.itemDelete', function(e) {
e.preventDefault();
$(e.target.parentElement).parent()[0].remove();
});

Categories