how can I append some html code using js several times - javascript

Hy guys, I have the following problem...
I need to draw in some cases (ex. when edit, save, update, etc) a defined html code (is the one at the end of the post) using js but I don't want to explicitly add the whole code every time i need it, so I'm wondering... ¿Is there a better way than adding it to a var and then .append or .html the code? instead of building the code every time I want to make it into an object oriented approach with properties and building it when needed, is there a way?
This is the code
for (var i = 1; i <= lineas; i++) {
c += "<div class='row'><section>some html for example</section></div>";
c += "<div>some other stuff</div>";
}
$('#res').append(c);
By a better way to do this... I mean... or I'm looking for, some kind of object oriented way to doit, hope you guys get it.
Regards!

If you mostly use the same formats, you can write an object that will define some commonly-used (in your judgement) helpers:
var helper = {
sectionInDiv: function(divClass, sectionText) {
var result = "<div class='" + divClass +"'><section>" + sectionText +"</section></div>";
return result;
},
divOnly: function(text) {
var result = "<div>" + text + "</div>";
return result;
},
repeat: function (n, htmlText){
var result = "";
for (var i = 1; i <= n; i++) {
result += htmlText;
}
return result;
}
...
}
and then use it:
var htmlToRepeat =
helper.sectionInDiv("row", "some example text") +
helper.divOnly("some more text");
helper.repeat(10, htmlToRepeat);

Related

How can I write this repetitive JS function more cleanly?

I have a function that will populate a table #contents with some rows. It looks like this:
// data is defined in data.js
function populateTable() {
for(var i = 0; i < data.length; i++) {
if (data[i].type == "MOV") {
var row = '<tr><td><a>' + data[i].title + '</a></td>';
row+= '<td><a>' + data[i].year+ '</a></td></tr>';
$("#contents").append(row);
} else {
var row = '<tr><td><a>' + data[i].title + '</a></td>';
row+= '<td>' + data[i].year+ '</td></tr>';
$("#contents").append(row);
}
}
}
I'm going to potentially have a bunch of content types, so this IF statement will get a good deal bigger, I'm wondering what a better way to handle the repeated elements inside the IF statement might be?
You do not need to repeat the string that do not vary for each if statement, That would shorten up your code a lot
function populateTable() {
function getUniqueString({type, year}) {
if(type == 'MOV')
return `<a>${year}</a>`
return `${year}`
}
for (const d of data) {
$("#contents").append(`<tr>
<td><a>${d.title}</a></td>
<td>${getUniqueString(d)}</td>
</tr>`)
}
}
Your using var so I am assuming your a beginner. So watch a youtube video or research : template strings and higher order functions.
Template strings or much cleaner way of writing strings because you don't have to use the + sign
Higher order functions are better and cleaner way of looping over arrays.
Oh it also does not hurt to use some line spacing. It really improves readability

How do you make an IIFE remember **each** instance of a variable when dealing with event listeners?

I'm working with a list of images that may change in number, so fixed IDs and event listeners are not practical. The below code produces the correct number of buttons with the correct IDs, but only the last one has a functional event listener.
for (var i = 0; i < amount; i++) {
!function(index) {
if (items[index].classList.contains('current')) {
document.getElementById('selectButtons').innerHTML += '<button id=\"bitems' + index + '\"> ⬤ <span class=\"offscreen\">Item ' + i + '</span></button>';
}
else
{
document.getElementById('selectButtons').innerHTML += '<button id=\"bitems' + index + '\"> ◯ <span class=\"offscreen\">Item ' + i + '</span></button>';
}
document.getElementById('bitems' + index).addEventListener("click", function(ev) {
alert("clicked");
});
}(i);
}
Apparently the IIFE is not storing the individual variables like it is supposed to, but I can't figure out why. After all, that is the entire purpose of an IIFE within a loop.
Any help would be greatly appreciated.
IIFE is working fine. Actually every time you update the innerHTML for selectButtons, the DOM is recreated, and all the events attached to it are gone!
Instead of updating the innerHTML in each iteration, you can append the buttons to it instead like:
for (var i = 0; i < 10; i++) {
!function(index) {
var button = document.createElement("BUTTON");
var t = document.createTextNode("Button" + index);
button.appendChild(t);
document.getElementById('selectButtons').appendChild(button);
button.addEventListener("click", function(ev) {
alert("clicked +" +index);
});
}(i);
}
Please do add the conditions around it that you need.
Every time you do innerHTML += you are replacing the entire HTML, which removes any previously installed event handlers. This is one perfectly good reason not to treat HTML as a bunch of strings that you innerHTML onto the page. Instead of strings, think in terms of elements, as in another answer. Then you also don't need to use IDs as a poor man's "variable name" to reference elements; you can just use the element itself.
You don't need a clumsy IIFE. That's what let is for.
Here's a cleaned-up version of your code:
var buttons = document.getElementById('selectButtons');
for (let i = 0; i < amount; i++) {
var current = items[i].classList.contains('current');
var button = document.createElement("BUTTON");
var bullet = document.createTextNode(current ? '◯' : '⬤')
var span = document.createElement('span');
van spanText = `Item ${i}`;
span.className = 'offscreen';
span.appendChild(spanText);
button.appendChild(bullet);
button.appendChild(span);
buttons.appendChild(button);
button.addEventListener('click', () => alert(`clicked ${i}`));
}
If you want to save a line or two, you could take advantage of the fact that appendChild returns the appended child, and chain:
buttons.appendChild(button).appendChild(span).appendChild(spanText);
If you're going to be doing a lot of this, it would be best to create some tiny utility routines:
function createElementWithText(tag, text) {
var b = document.createElement(tag);
var t = document.createTextNode(text);
b.appendChild(t);
return b;
}
function button(text) { return createElementWithText('button', text); }
function span(text) { return createElementWithText('span', text); }
Now you can write your code more concisely as:
var buttons = document.getElementById('selectButtons');
for (let i = 0; i < amount; i++) {
var current = items[i].classList.contains('current');
var button = button(current ? '◯' : '⬤');
var span = span(`Item ${i}`);
span.className = 'offscreen';
buttons.appendChild(button).appendChild(span);
button.addEventListener('click', () => alert(`clicked ${i}`));
}
Actually, it would moderately preferable to create a document fragment, add all the buttons to it in advance, then insert it into the DOM a single time.
However, in practice, you would be better off using some kind of templating language, in which you could write something like:
<div id="selectButtons">
{{for i upto amount}}
<button {{listen 'click' clicked}}>
{{if items[i] hasClass 'current'}}◯{{else}}⬤{{endIf}}
<span class="offscreen">Index {{i}}</span>
</button>
{{endFor}}
</div>
It's beyond the scope of this answer to recommend a particular templating language. There are many good ones out there, such as Mustache, that google can help you find, with a search such as "javascript templating languages".

Node.js loops and JSON building

Respected ppl ....
This is my node.js code ...
https://gist.github.com/SkyKOG/99d47dbe5a2cec97426b
Im trying to parse the data of our exam results ...example ...
http://www.vtualerts.com/results/get_res.php?usn=1MV09IS002&sem=7
Im getting the results ...and i am traversing back for previous seems too ...
All works but traversing back happens in random ... prolly something wrong with the loops ...
json.results = [];
var output = '';
var k = response.query.results.body.div.div[0].table[1].tr.length;
for (var j = 1; j < k; j++) {
for (var i = 0; i <= 5; i++) {
var result_obj = {};
result_obj.subjects = [];
for (key in response.query.results.body.div.div[0].table[1].tr[j].td[i]) {
if (typeof response.query.results.body.div.div[0].table[1].tr[j].td[i].em === "undefined") {
continue;
}
var subject_obj = {};
output += "Subject : " + response.query.results.body.div.div[0].table[1].tr[j].td[i].em + " " + "\n";
var subtext = response.query.results.body.div.div[0].table[1].tr[j].td[i].em + " " + "\n";
subject_obj.subjectname = subtext.replace(/[(].*[)]/, "").trim();
result_obj.subjects.push(subject_obj);
console.log(subject_obj);
break;
}
console.log(result_obj.subjects);
I presume there is something like async concepts which need to implemented correctly to make the reordering of sems in correct order ...
And to get the JSON in this format ...
https://gist.github.com/SkyKOG/3845d6a94cea3b744296
I dont think im pushing the created objects at the right scope ...
Kindly help in this regard .... thank you ...
(I'll answer the ordering part. Suggest making the JSON issue a separate question to fit in with the Q&A format.)
When you make the HTTP request in your code (see the line below) you're bringing a varying delay into the order that responses are executed
new YQL.exec(queryname, function (response) {
You need to track the order of the requests yourself, or use a library to do it for you.
Code it yourself
In order to get around that you need something that keeps track of the original order of the requests. Because of the way closures work you can't just increment a simple counter because it'll be changed in the global scope as your loop progresses. The idiomatic way to solve this is by passing the counter into an immediately executed function (as a value type)
e.g.
var responseData = [];
for ( var i = 0; i < 100; i++ ){
(function(){
...
// http call goes in here somewhere
responseData[i] = data_from_this_response
...
})(i)
}
Use a library
Check out the async.parallel() call in Caolan's excellent library. You pass it an array of functions and it'll return to your callback with an array of the results.
https://github.com/caolan/async/#parallel
You'll need to create a loop that populates the array with curried versions of your function containing the appropriated variables.

Show/Hide image before filtering a table isn't working

I have web page with some really large tables that I'm filtering using some jquery routines I wrote. Anyway, when these tables get really large and the filtering functions can take some time to complete. So I figured I'd unhide a animated gif so the user had some feedback. However, the gif never appears when I call:
$('#loadingimg').show();
Unless I put an alert statement in front of it. I apologize for the ugly code, I'm not an experienced jquery/javascript programmer.
function filter()
{
var eles = ["mtmprogram","rate","stage"];
var tag;
var classes='';
$('#loadingimg').show();
//alert('hi');
$('.report').hide();
for (var i in eles)
{
tag = '#' + eles[i] + ' option:selected';
if ($(tag).val())
{
//$('.'+ $(tag).val()).show();
classes = classes + '.' + $(tag).val();
}
}
if (classes == '')
$('tr.report').show();
else
$(classes).show();
filterSubtables('Loan Number');
$('#loadingimg').hide();
}
Many thanks!
Maybe you aren't giving the #loadingimg element enough time to display. You could test this by running the rest of your code in a timeout:
function filter()
{
var eles = ["mtmprogram","rate","stage"],
classes = '';
$('#loadingimg').show();
//alert('hi');
setTimeout(function () {
$('.report').hide();
for (var i = 0, len = eles.length; i < len; i++)
{
var $tag = $('#' + eles[i] + ' option:selected');
if ($tag.val())
{
//$('.'+ $tag.val()).show();
classes = classes + '.' + $tag.val();
}
}
if (classes == '')
$('.report').show();
else
$(classes).show();
filterSubtables('Loan Number');
$('#loadingimg').hide();
}, 500);
}
Notice that I changed how the tag variable is used (this creates less CPU overhead to make less jQuery selections and to use as local a variable as possible). I also changed your loop to a better format that performs amazingly faster than for ( a in b ): http://jsperf.com/jquery-each-vs-for-loops/2

How to optimize jquery grep method on 30k records array

Is it possible to optimize this code? I have very low performance on this keyup event.
$('#opis').keyup(function () {
if ($('#opis').val() != "") {
var search = $.grep(
svgs, function (value) {
reg = new RegExp('^' + $('#opis').val(), 'i');
return value.match(reg) == null;
}, true);
$('#file_list').html("");
var tohtml = "";
$cnt = 0;
for (var i = 0; i < search.length; i++) {
if ($cnt <= 30) {
tohtml += "<li class='file_item'><a href='' class='preview'>" + search[i] + "</a> <a href='" + search[i] + "' class='print_file'><img src='img/add16px.png' alt='dodaj'/></li></a>";
$cnt++;
} else {
break;
}
}
$('#file_list').html(tohtml);
$(".preview").click(function () {
$('#file_preview').html('<embed src="opisy/' + $(this).html() + '" type="image/svg+xml" pluginspage="http://www.adobe.com/svg/viewer/install/" /> ');
$(".preview").parent().removeClass("selected");
$(this).parent().addClass("selected");
return false;
});
$(".print_file").click(function () {
if (jQuery.inArray($(this).attr('href'), prints) == -1) {
$('#print_list').append('<li>' + $(this).attr('href') + '</li>');
prints.push($(this).attr('href'));
} else {
alert("Plik znajduje się już na liście do wydruku!");
}
return false;
});
} else {
$('#file_list').html(" ");
}
});
var opis = $('#opis')[0]; // this line can go outside of keyup
var search = [];
var re = new RegExp('^' + opis.value, 'i');
for (var i = 0, len = svgs.length; i < len; i++) {
if (re.test(svgs[i])) {
search.push(svgs[i]);
}
}
It's up to 100x faster in Google Chrome, 60x in IE 6.
first thing you have to learn:
$('#opis').keyup(function() {
$this = $(this);
if($this.val()!=""){
// so *$this* instead of *$('#opis')*
// because you are reperforming a *getElementById("opis")* and you've already called it when you used the keyup method.
// and use $this instead of $(this) | pretty much the same problem
so about the grep function, maybe if you cache the results it would help in further searchs I guess, but I don't know if can help you with that
Well the thing with javascript is that it executes under the users environment and not the servers environment so optimization always varies, with large large arrays that need extensive work done on them to I would prefer to handle this server side.
Have you thought about serializing the data and passing them over to your server side, which would handle all the data calculations / modifications and return the prepared result back as the response.
You may also want to take alook at SE:Code Review for more optimization advise.
Some optimization, tips:
if($('#opis').val()!=""){ should be using '!=='.
return value.match(reg)==null; should be ===.
for(var i=0;i<search.length;i++){
reg = new RegExp(...); should be var reg ... as its not defined outside the function as a global.
Move all your variable declarations to the top of the function such as
var i,cnt,search,tohtml etc
i would advise you to start using Google Chrome, it has a built in system for memeory tracking on perticular tabs, you can go to the url about:memory in chrome, which would produce a result like so:
Image taken from: http://malektips.com/google-chrome-memory-usage.html
Each time you perform the grep, you are calling the 'matching' function once per array entry.
The matching function creates a RegExp object and then uses it to perform the match.
There are two ways you could improve this:
Create the RegExp once, outside of the function, and then use a closure to capture it inside the function, so that you don't have to keep recreating the object over and over.
It looks like all you're trying to do is to perform a case-insensitive tests to see whether the sought string is the start of a member of your array. It may be faster to do this more explicitly, using .toUpper and substring. However, that's a guess and you should test to find out.

Categories

Resources