What's the best way of appending an array of jQuery elements to the page?
I know that if I'm appending straight HTML tags then they should be collected together in an array then the array appended. This is the fastest method. However I have a collection of jQuery elements that have events created on them so I can't just concatenate strings together.
Example:
function displayCommentActions(actions) {
var html = [];
module.$element.find('.comments .actions li').remove();
$.each(actions, function(key, value) {
html.push($('<li class="' + key + '">' + value.name + '</li>').bind({
click: function(event) {
alert('found click');
}
}));
});
$.each(html, function(count, item) {
module.$element.find('.comments .actions').append(item);
})
};
This is ugly because it takes two loops. One to create the jQuery objects and one to output them to the page. How can I improve my code?
#efritz, doing .remove() on every list item is slow and expensive, a quick wipe of list items would be .empty().
#Samuel you can't avoid looping here. .append() takes either a string (of html) or a jquery object. It can't take an array of jquery objects.
var actionsList = module.$element.find('.comments .actions');
actionsList.empty() // Remove all contents of our <UL>
$.each(actions, function(class, value) {
var listItem = $('<li />').addClass(class) // Create our <li> and add our class
.append('' + value.name + '') // Add our link
.click(function() { // Click bind event
alert('Clicked item');
});
actionsList.append(listItem); // Add our <li> to our <ul>
}
This is probably as small as you're gonna get it Samuel, it's an improvement from your dual-loop at least.
Would something like this not work?
function displayCommentActions(actions) {
var target = module.$element.find('.comments .actions li')
target.remove();
$.each(actions, function(k, v) {
var item = $('<li />').addClass(key);
var link = $('<a />')
.attr('href', 'javascript:void(0);')
.html(value.name)
.click(function() {
alert('found click');
});
elem.append(item);
target.append(item);
}
}
Also, remember that repeated selectors can be expensive - store them if at all possible.
Related
I am fairly new to JS, and have created a little piece of script and it does exactly what I want which is find some elements then adds elements with data populated from via ajax....
So I go from this...
<select><select/>
to this...
<select>
<option value="{ajax value data}"> {ajax text data} <option/>
...
<select/>
using this piece of script...
filteredSelectIds.forEach(function (item) {
let itemId = '#' + item;
let itemData = item.split('-')[0] + 's';
$.each(data[itemData], function (i, color) {
$(itemId).append($('<option/>', {
value: color.optionValue,
text : color.optionText
}));
});
});
Now, what I am trying to do is at the same time add a Font Awesome icon to each element so I need to end up with something like this,,,,
<select>
<option value="{ajax value data}"><i class="fa fa-icon"> {ajax text data} <i/><option/>
...
<select/>
How would I do that??
I'm also new at JS, try this.
element = '<i class="fa fa-icon"> {0} <i/>'.format("{ajax text data}")
$('<option/>').append( element );
So #brk gave me this solution which worked, and would work for putting an Element inside another
"Create the option tag & i tag & first append itag to option tag and then append option tag to item"
filteredSelectIds.forEach(function (item) {
let itemId = '#' + item;
let itemData = item.split('-')[0] + 's';
$.each(data[itemData], function (i, color) {
var selOption = $('<option value="' + color.optionValue + '"></option>');
selOption.append('<i class="fa fa-icon">'+color.optionText+'<i/>');
$(itemId).append(selOption); }); });
However, although this placed the element inside the element as I wanted, and this could principle could probably be used to place any element within another, Tibrogargan correctly pointed to a question that makes the point that elements cannot be place within elements (Not really the Point of my question, but helpful). My solution was simply using the unicode for the Font Awesome icon and escaping it with \u then used \xa0 for additional spaces as follows:-
filteredSelectIds.forEach(function (item) {
let itemId = '#' + item;
let itemData = item.split('-')[0] + 's';
$.each(data[itemData], function (i, color) {
$(itemId).append($('<option/>', {
value: color.optionValue,
text : '\ue905 \xa0\xa0\xa0' +color.optionText
}));
});
});
Thanks!
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>
I have two unordered lists. One unordered lists get populated dynamically and you double click it's items to add to the other unordered list.
I'm trying to figure out how to detect if the item on the dynamically populated list isn't already in the other list. If it is. then it shouldn't get added again. Don't want to add duplicate items.
The code that populates the ul:
.done(function(html) {
var results = jQuery.parseJSON(html);
$('#store_sel').children().remove();
for (var i = 0; i <= results.length; i++){
$("#store_selection").append("<li class='" + results[i].name + " stores'" + "id= " + results[i].uuid +
+ ">" + results[i].name + "</li>");
}
});
The event:
$('#store_selection').on('dblclick', 'li', function(e) {
e.stopPropagation();
e.preventDefault();
if ($('#store_selected').find($(this))) {
console.log('item already there');
} else {
$('#store_selected').append($(this).clone());
//$(this).remove();
}
});
EDIT: Why isn't this working? It basically goes to console.log even if the ul is empty.
There are several issues within your if statement.
An element can't be in 2 places at the same time. Therefore you can't locate this in 2 lists
An object will always return truthy. Use length of returned selector collection instead
Since ID's must be unique in a page I suggest you use a data- attribute to store the UUID
<li data-uuid="xxxx-yyy">
Then when you search:
var uuid = $(this).data('uuid')
if ($('#store_selected').find('.stores[data-uuid='+uuid+']').length) {
console.log('item already there');
} else {
$('#store_selected').append($(this).clone());
//$(this).remove();
}
I have added an .on() function to my dynamically added list elements with jQuery. The problem I'm facing is that the button that triggers the click lives inside the dynamically added li elements and therefore
$(this).parent().text()
returns the text from the li element as well as the button's text.
See the following fiddle: http://jsfiddle.net/TL5TR/
How can I get around this problem and only display the li text (without the button text)? Would I have to rework my code in a way that button will be placed outside the li tag?
Update
Before I accept one of these answers -- all of them which are working by the way, so thank you, can you also explain to me what are the pros/cons of using one method or the other - i.e. textNode vs span tag.
I think the easiest solution would be to add span around your text:
$('#list').append("<li><span>" + item + "</span><button class='clickme'>Click me</button></li>");
And then:
$(this).siblings("span").text()
If the textNode is the previous sibling of the clicked button you can use previousSibling property.
$("#list").on('click', 'button', function() {
alert(this.previousSibling.nodeValue);
});
http://jsfiddle.net/cZk8H/
var array = ["First", "Second", "Third"];
$.each(array, function(index, item) {
$('#list').append("<li><span>" + item + "</span> <button class='clickme'>Click me</button></li>");
});
$("#list").on('click', 'button', function() {
alert($(this).parent().find("span").text());
});
put the text in a span tag
http://jsfiddle.net/TL5TR/2/
Try Adding text wrapped in a span. And you should easily address the sibling's value, later:
var array = ["First", "Second", "Third"];
$.each(array, function(index, item) {
$('#list').append("<li><span>" + item + "</span><button class='clickme'>Click me</button></li>");
});
$("#list").on('click', 'button', function() {
alert($(this).siblings().text());
});
http://jsfiddle.net/TL5TR/1/
PROS (of using span) : Better Management of text inside the li. You actually refer to the content enclosed in the span always....
PROS(of not using span) : Lesser tags in the HTML that you need to take care of. Although in this case I would say it's more of a micro management to think about it.
Or instead of separating it out, you could just do this:
var array = ["First", "Second", "Third"];
$.each(array, function(index, item) {
$("<li>" + item + " <button class='clickme'>Click me</button></li>")
.appendTo('#list')
.on('click', function() { alert(item); });
});
Unless of course you are adding these some other way. Then I would use one of the other suggested answers.
You can clone the element and remove the children elements:
$(this).parent().clone().children().remove().end().text()
I am reading contents from JSON file and adding to div with unique IDs. I need to call jquery slide down effects to each div. Lets us consider the case, on clicking (div id=A1) it should slide down and show (div id=B1), in that way I have div with IDs A(1..N) and B(1..N).
var i=1;
$.each(items, function (index, item) {
$(document).ready(function(){
$("#A"+i).click(function(){
$("#B"+i).slideToggle();
});
});
$("#allContents").append('<div id="A'+i+'">' + item.Name + '</div>');
$("#allContents").append('<div id="B'+i+'">' + item.Details + '</div>');
i++;
});
This is the closest code I could derive to, but it is not working. If anyone could help me fix or suggest a better way to get this thing working it would be great. Thanks a lot!
$('#allContents').on('click', 'div[id^=A]', function() {
$('div#B' + this.id.replace('A','')).slideToggle();
});
A little explain
div[id^=A] point out those div whose id start with A.
this.id retrieve the id of clicked element.
this.id.replace('A','') replace A form the id and get the numeric index which equal to index of B.
$('div#B' + this.id.replace('A','')) point to element id=B1, id=B2 and so on.
Full code
// you can bind event handler outside ot loop
$('#allContents').on('click', 'div[id^=A]', function() {
$('div#B' + this.id.replace('A','')).slideToggle();
});
$.each(items, function(index, item) {
$("#allContents").append('<div id="A' + i + '">' + item.name + '</div>');
$("#allContents").append('<div id="B' + i + '">' + item.Details + '</div>');
i++;
});
Working Sample
Note
As you're creating div from A(1..N) and B(1..N) dynamically so you need delegate event handler (aka live event).
Syntax of jQuery .on() for delegate event is like following:
$(container).on(eventName, target, handlerFunction)