What is the fastest way to build HTML from JavaScript? - javascript

I am working on a website that uses infinite scroll. There is a function called is_element_in_view() that gets executed on these 3 events:
scroll
load
resize
The function does exactly what it's called, it checks to see if an element with a loading gif image is in view and if so it fires an ajax request to get content from the server.
The server sends back a json object that looks like this:
[{
"url": "\/embed\/182926\/some-slug",
"thumb": "http:\/\/cdn.site.com\/91\/26\/a62c1ad74327321dab78bb194c130da5.jpg",
"type": "video",
"is_original": false,
"is_prank_news": false,
"title": "Hello World",
"description": "\t<p>Enjoy this video!<\/p>",
"teaser": "Click Me!",
"finder": "Found by <strong>Jim<\/strong> yesterday",
"likes": "2 likes",
"ad_img": null,
"media_stats": "<div class=\"media-status\">2000 views<\/div>"
},
more objects...]
There's only one object in this response for clarity sake but in reality I get back 20. This is how I'm building out the html from the json data:
$.ajax({
url: '/some/ajax/url',
type: 'get',
data: 'somedata',
dataType: 'json',
success: function(response) {
if(!$.isEmptyObject(response)) {
for(var i = 0; i < response.length; i++) {
if(response[i]) {
var item = response[i];
var title = item.title.replace(/\\"/g, '"');
var media_label = '';
var item_description_teaser = '';
var likes = '';
var ad_image = '';
var media_stats = '';
if(item.description) {
// description
item_description_teaser = '<div class="description">' + item.description.replace(/\\"/g, '"');
// teaser
item_description_teaser += (item.teaser) ? ''+ item.teaser.replace(/\\"/g, '"') +'<img src="images/teaser-arrow.png" alt="" /></div>' : '</div>';
}
// media label
if(item.type == 'article' && item.is_prank_news || item.is_original && item.is_prank_news) {
media_label = '<span class="media-label prank-news-network">Prank</span>';
}
else {
if(item.type == 'article') {
media_label = '<span class="media-label article">Article</span>';
}
else if(item.is_original) {
media_label = '<span class="media-label original">Original</span>';
}
}
// likes
if(!settings.hide_likes) {
likes = '<span class="likes">' + item.likes + '</span> | ';
}
// ad image
if(item.ad_img) {
ad_image = '<img src="'+ item.ad_img +'" alt="" class="ad-img" />';
}
block += '<article class="block">' +
'<div class="inner-left">' +
media_label +
'<a href="'+ item.url +'" title="" class="thumb">' +
'<img src="'+ item.thumb +'" alt="" width="198" height="111" />' +
'</a>' +
'</div>' +
'<div class="inner-right">' +
'<a href="'+ item.url +'" title="" class="title">' +
title +
'</a>' +
item_description_teaser.replace(/\\"/g, '"') +
'<div class="media-stats">' +
likes +
'<span class="finder">'+ item.finder.replace(/\\"/g, '"') +'</span>' +
'</div>' +
ad_image +
'</div>' +
item.media_stats +
'</article>';
}
}
$('#content').append('<div class="page page-'+ page_num +'">' + block + '</div>');
// update page count
page_num++;
// clear previous listings
block = '';
}
else {
$('#content').append('<div class="page page-1"><p class="nothing-to-show">Nothing found...</p></div>');
}
},
error: function() {
alert('error');
}
});
As you can see I put everything in one giant string stored inside the variable block. I append data to this string with every loop and append it to the page outside the loop at the end.
I feel like there is a faster way to build html from js. I read somewhere a while ago that building giant strings like I'm doing isn't as efficient as some other method the article described that I forgot. So what's the faster way to do this?

Store the blocks in an array say blocks, then
$('#content').append(blocks.join(""));
Edit: that wasn't what the OP wanted. I guess the problem is appending the stuff each time the event is triggered.
I'd say to create a DocumentFragment, put the new stuff in it, then appending to $("#content"). Unfortunately, DocumentFragments don't support innerHTML.
So, create a dummy element, fill it and then put its child nodes into the container:
var dummy = $("<div>").html(block), content = $("#content");
$.each(dummy.children(), function(i, c) {content.append(c);});

Adding html elements to the DOM represents a big performance penalty so it is better to create a big string and append it at the end, this post explains it really well
For most of your uses, the method of creating one really long string and appending it at the end will be the best choice, as it makes the best use of the trade offs of code legibility, ease of programming, and speed.

You could have your server return the values already marked-up in HTML, then:
$('#content').append( response );
You can then handle all of your looping and filtering server side, cutting down on the amount JS in your document.

Related

Why is my json get request not working?

I am working on building a movie search app. It is my first time using json. I cannot figure out why my code is not working. I have it running on localhost using xampp.
On submit
$('.search-form').submit(function (evt) {
// body...
evt.preventDefault();
var $searchBar = $('#search');
var omdbApi = 'http://www.omdbapi.com/?';
var movieSearchTerm = $searchBar.val();
var searchData = {
s:movieSearchTerm,
r:json
}
Here is the callback function
function displayMovies(data) {
// for each search result
$.each(data.items,function(i,movie) {
movieHTML += '<li class="desc">';
//movie title
movieHTML += '<a href="' + movie.Title + '" class="movie-title">';
//release year
movieHTML += '<a href="' + movie.Year + '" class="movie-year">';
//poster
movieHTML += '<img src="' + movie.Poster + '" class="movie-poster"></li>';
$('#movies').html(movieHTML);
}); // end each
// movieHTML += '</li>';
}
$.getJSON(omdbApi, searchData, displayMovies);
});//end submit
r:json
You made a typo.
You haven't created a variable called json and the service expects the value of r to be json.
String literals need to be surrounded with a pair of " or '.
data.items
And the JSON returned doesn't have items, it has Search.

Outputting data from an AJAX call to HTML

I'm using AJAX to add more articles to a list of articles when you press a button. So my AJAX call returns data that includes a title, author and 1 to 3 images associated with the article. Below is the code I'm using to output it, but it feels VERY clunky.
What are the best practices for printing out HTML with JavaScript/jQuery in a scenario like this where I need to add many new tags with new information? Thanks for the help!
Also, I know some of the code isn't super well written because it's a first draft just to make stuff work, so please only answer this question with regards to printing out the HTML or things that will make printing the HTML easier
$j.getJSON(ajaxurl, {action: 'load_articles', issues: $issues}, function(data) {
if (data.message != null) {
alert(data.message);
return
}
list = $j('.all-articles ul');
for (i in data.articles) {
article = data.articles[i];
//Hides articles already on page
if ($j("#" + article.id).size() === 0) {
list.append('<li class="article-preview" id="' + article.id + '">' +
'<h3 class="article-headline">' + article.title + '</h3>' +
'</li>');
current = $j("#" + article.id)
current.append('<p class="authors"></p>');
authors = $j("#" + article.id + " .authors")
for (a in article.authors) {
authors.append(article.authors[a].data.display_name + " ");
}
current.append('<div class="images"></div>');
images = $j("#" + article.id + " .images")
for (i in article.image) {
text = "<div class='image-expand-container'>";
if (i == 0) {
text += ('<img id="' + article.image[i].id + '"class="selected" src="' + article.image[i].medium + '"></img>');
}
else {
text += ('<img id="' + article.image[i].id + '" src="' + article.image[i].medium + '"></img>');
}
text += '<div class="dashicons dashicons-editor-expand"></div></div>';
images.append(text);
}
}
}
There are a few approaches you can take.
As you're doing here, you can return data from your ajax call (e.g. as JSON) and then use a javascript function to generate the corresponding HTML by building strings. This, as you're finding, is often messy.
You can generate the actual HTML on the server side, and have the ajax call return an HTML fragment, which you insert into your DOM. This has the advantage that, if some of your HTML is loading when the page loads, and some is loading via ajax, you can use the same approach (PHP, XSLT, ASP.NET Razor, any kind of server-side templating) to generate all of the HTML.
You can use a javascript templating framework to turn your JSON data into HTML. If all of your HTML is being generated via javascript (e.g. in a single-page application) this may be your best bet.

'no image' icon while outputting AJAX-delivered content [duplicate]

This question already has answers here:
How to show Page Loading div until the page has finished loading?
(13 answers)
Closed 8 years ago.
I use AJAX requests to retrieve data from mySQL (link), after that I build a sequence of post. Every post has a feature image. So while in process of outputting the posts, the visitor sees 'no image' sign:
.
This lasts for a bit of second, but looks bad.
How can I prevent this sign from showing, or subtitute with a loading spin?
jQuery part for outputting the results
function postsBuilder(posts){
var contents ='';
$.each(posts, function(k, field){
if(field.link[field.link.length-1] == '/') {field.link = field.link.slice(0, field.link.length-1)};
contents += '<div class="post-container">';
//here I output the feature image
contents += "<div class='col_1'><div class='post_image'><img src='/images/icons/id_" + field.id + ".png'></div></div>";
contents += "<div class='col_2'><div class='post_name'><a class='post_name_link' target='blank' href=http://" + field.link + ">" + field.name + "</a></div>";
contents += "<div class='post_link'>" + field.link + "</div>";
contents += "<div class='post_content'>Content: " + field.content + "</div>";
if ( field.video.indexOf('you') >-1 ) {contents += "<div class='post_video'><a data-toggle='lightbox' href=" + field.video + ">Video</a></div>";}
contents += "</div></div><br />";
});
return contents;
}
One of examples when the function is called
$.ajax({cache:false,
url:str,
beforeSend: function() { $('#loadingDiv').show(); },
complete: function() { $('#loadingDiv').hide(); },
dataType :'json'})
.done(function(result){
var i, j, m=0;
var div_content="";
div_content += "<div><b>Category: ".concat(result[0].category).concat("</b></div>");
posts_array = [];
for (i=0,j=result.length; i<j; i+=size) { // slice whole JSON result into portions of at maximum 'size' posts in each
posts_array.push(result.slice(i, i+size));
m++;
}
div_content += postsBuilder(posts_array[0]);
...
$("#content").html(div_content);
});
Use the error event for images:
$(document).on('error', 'img', function () {
$(this).hide();
// or $(this).css({visibility:"hidden"});
});
Check out How to silently hide "Image not found" icon when src source image is not found

using multiple clauses in if in jQuery template

I want to use a variable in the if clause which is in jQuery template. Console log says:
Uncaught Syntax Error: Unexpected token {
Here is my code:
var isActive = true;
var isPasive = false;
var isGuest = false;
var tmp = '<script>' +
'{{each hastalar}}' +
'<a href="#" class="patientRow" data-name="${$value.M_AdiSoyadi}" data-tc="${$value.M_TcKimlikNo}" data-tahlilgunu="${$value.M_TahlilGunu}"> ${M_AdiSoyadi}' +
'{{if $value.M_HastaBulunmaDurumu == "1" && ${isActive} }}' +
'<img id="imgMember_${$value.M_TcKimlikNo}" src="images/greenmember.png" title="Hasta klinik ve DYOB kayıtlarıyla örtüşüyor." style="width:15px;height:15px;"/>' +
'{{else $value.M_HastaBulunmaDurumu == "2" }}' +
'<img id="imgMember_${M_TcKimlikNo}" src="images/bluemember.png" title="Hasta kliniğinizde mevcut fakat DYOB sisteminde sizin kliniğinizde görünmüyor. Lütfen DYOB sistmine hasta kaydını yapınız." style="width:15px;height:15px;"/>' +
'{{else $value.M_HastaBulunmaDurumu == "3"}}' +
'<img id="imgMember_${M_TcKimlikNo}" src="images/redmember.png" title="Hasta kliniğinizde mevcut değil fakat DYOB sisteminde sizin kliniğinizde görünüyor. Lütfen kan tahlili yapılan hastaların listesini kontrol ediniz." style="width:15px;height:15px;"/>' +
'{{/if}}' +
'<img id="imgResult_${$value.M_TcKimlikNo}"/>' +
'<img id="imgInfo_${$value.M_TcKimlikNo}"/>' +
'</a>' +
'{{/each}}' +
'</script>';
I use jquery.tmpl.min.js. What should I do to use the variable in if clause?
For a generic approach of replacing your variables in JavaScript strings you could you the following snippet;
var tmp = '<script>' +
... +
'<\/script>'
.replace('${isActive}', isActive)
.replace('${isPassive}', isPassive)
.replace('${isGuest}', isGuest)
For a more detailed solution on your problem, we need information about the framework and template engine which you are using.
EDIT:
I noticed that the closing script tag causes an error. YOu might want to escape the closing script tag at the like so:
<\/script>

Limit number of Dynamic list Items in a Function

I would like to achieve 2 things with this Code I have been working on so not sure if to separate the Questions:
JS:
function listPosts(data) {
postlimit =
var output='<ul data-role="listview" data-filter="true">';
$.each(data.posts,function(key,val) {
output += '<li>';
output += '<a href="#devotionpost" onclick="showPost(' + val.id + ')">';
output += '<h3>' + val.title + '</h3>';
output += '<p>' + excerpt + '</p>';
output += '</a>';
output += '</li>';
}); // go through each post
output+='</ul>';
$('#postlist').html(output);
} // lists all the posts
Questions:
1: I would like to limit the number of Dynamic List Posts returned to 8
2: While I limit the displayed items, I want to add a 'More...' text at the bottom so another set of 8 items is appended to already displayed list.
I am already trying out some codes but was hoping to get some guidance
function listPosts(data, postlimit) {
var $output = $('<ul class="posts" data-role="listview" data-filter="true">');
$.each(data.posts,function(key, val) {
$("<li>", {id: "post_" + val.id})
.append([
$("<h3>", {text: val.title}),
$("<p>", {text: val.excerpt})
])
.appendTo($output);
return (postlimit-- > 1);
});
$('#postlist').empty().append($output);
}
// exemplary delegated event handler
$(document).on("click", "ul.posts h3", function () {
$(this).show();
});
later ...
listPosts(data, 8);
Notes:
from $.each() you can return true or false. If you return false, the loop stops.
Try not to build HTML from concatenated strings. This is prone to XSS vulnerabilities that are easy to avoid. jQuery gives you the tools to build HTML safely.
Generally, for the same reason, try to avoid working with .html(), especially if you already have DOM elements to work with.
Don't use inline event handlers like onclick. At all. Ever.
I am answering you on basis of pure logic and implementation of logic. there could be API stuff for it , but I don't really know. Secondly; It would be a good solution to find some jQuery plugin if you don't have any problems with using jQuery.
call the function onMoreClick() upon clicking the More... html item
var end = 8;
var start = 1;
function onMoreClick()
{
start = end
end = end+8;
listPosts(data)
}
function listPosts(data) {
postlimit =
var output='<ul data-role="listview" data-filter="true">';
var i = start;
$.each(data.posts,function(key,val) {
if(i<end && i >=start){
output += '<li>';
output += '<a href="#devotionpost" onclick="showPost(' + val.id + ')">';
output += '<h3>' + val.title + '</h3>';
output += '<p>' + excerpt + '</p>';
output += '</a>';
output += '</li>';
i++;
}
}); // go through each post
output+='</ul>';
$('#postlist').html(output);
} // lists all the posts

Categories

Resources