Optimize nested loop to create HTML elements using jQuery - javascript

I'm developing an onscreen keyboard using HTML / Javascript. The keyboard is generated at runtime.
I'm using a JSON datastructure to store the different keyboard layouts, language settings etc.
var initParams = {
keyboardLanguage: "de",
keyboardLayout: {
de: {
row1: ["Q","W","E","R","T","Z","U","I","O","P"],
row2: ["A","S","D","F","G","H","J","K","L"],
row3: ["Y","X","C","V","B","N","M"]
},
en: {
row1: ["Q","W","E","R","T","Y","U","I","O","P"],
row2: ["A","S","D","F","G","H","J","K","L"],
row3: ["Z","X","C","V","B","N","M"]
}
}
}
And here is the function that does the job:
function CreateKeyHtmlElements(){
var divElement = document.createElement("div");
for(var obj in initParams.keyboardLayout[initParams.keyboardLanguage]){
var keyRow = divElement.cloneNode(false);
keyRow.id = "key" + obj;
keyRow.className = "keyboard-key-row";
for(var i = 0, l = initParams.keyboardLayout[initParams.keyboardLanguage][obj].length; i < l; i +=1){
var key = divElement.cloneNode(false);
key.id = "key-" + initParams.keyboardLayout[initParams.keyboardLanguage][obj][i];
key.className = "keyboard-key";
key.textContent = initParams.keyboardLayout[initParams.keyboardLanguage][obj][i];
keyRow.appendChild(key);
$(key).appendTo(keyRow);
}
$(keyRow).appendTo($("#keyboard-key-box"));
}
}
Are there any effective ways to optimize this function, particularly the loops, using jQuery.
Thanks in advance.

Actually there is a special HTML element for aggregation of html, not to make actual dom manipulations
var fragment = document.createDocumentFragment();
But as long as the benchmarks don't show actual difference between the fragment approach and inserting sting via innerHTML (proof)
You can do something like this:
var result = '<div class="keyboard">';
$(initParams.keyboardLayout[initParams.keyboardLanguage]).each(function(i,obj){
result += '<div class="row">';
result += '<div class="key">'+obj.join('</div class="key"><div>')+'</div>';
result += '</div>';
})
$(".target").append(result);
This doesn't give you a chance to add ID's but that actually dosn't matter, just when binding use $(el).index() to get the column index and $(el).parent().index() to get row

Related

page doesn't display anything

so I wrote a script to display 5 random arrays, but the page doesn't display anything.
here's the code:
<html>
<head>
<script>
function start(){
var arr(5),result;
result=document.getElementById("arraying");
result="<p>";
for(var i=0; i<5;i++){
arr[i]=Math.floor(Math.random()*10);
result+="arr["+i+"]= "+arr[i]+"</p><p>";
}
result+="</p>";
}
window.addEventListener("load",start,false);
</script>
</head>
<body>
<div id="arraying"></div>
</body>
</html>
I tried removing result=document.getElementById and write document.getElementById.innerHTML=result in the end of the function but didn't work. what's the error?
You cannot use the same variable for different purposes at the same time. First you assign a DOM element to result, and immediately on the next line you overwrite result with a string.
Build a string htmlStr inside your loop, and when that is done, assign this string to result.innerHTML property:
function start() {
let arr = [],
result, htmlStr = '';
result = document.getElementById("arraying");
htmlStr += "<p>";
for (let i = 0; i < 5; i++) {
arr[i] = Math.floor(Math.random() * 10);
htmlStr += "arr[" + i + "]= " + arr[i] + "</p><p>";
}
htmlStr += "</p>";
result.innerHTML = htmlStr;
}
window.addEventListener("load", start, false);
<div id="arraying"></div>
Looking at the code you seem to be missing some basic javascript concepts.
array size
This is probably your main issue:
var arr(5)
This does not make sense in javascript. Array length does not need to be predefined since all arrays are of dynamic length. Simply define an array like this:
var arr = []
Then later when you want to append new elements use push like this:
arr.push( Math.floor(Math.random()*10) )
adding html using innerHTML
There are different ways to dynamically inject html into your page. (It looks like) you tried to append the html as a string to the parent element. This is not possible.
You said you tried using innerHTML. That should work if used correctly.
A working implementation would work like this:
function start() {
var arr = []
var result = "<p>"
for(var i = 0; i < 5; i++) {
arr.push( Math.floor(Math.random()*10) ) // Btw this array isn't actually needed.
result += "arr[" + i + "] = " + arr[i] + "</p><p>"
}
document.getElementById("arraying").innerHTML = result
}
window.addEventListener("load", start, {passive: true});
adding html using createElement
A generally better way of dynamically adding html elements is via createElement.
This way you dont have to write html and are therefore less prone for making errors. It is also more performant and easier to integrate into javascript.
I think the best explaination is a commented implementation:
function start() {
var myDiv = document.getElementById("arraying") // get parent node
var arr = []
for(var i = 0; i < 5; i++) {
arr.push( Math.floor(Math.random()*10) )
var p = document.createElement("p") // create p element
p.innerText = "arr[" + i + "] = " + arr[i] // add text content to p element
myDiv.append(p) // append p element to parent element
}
}
window.addEventListener("load", start, {passive: true});
small tips
The let keyword works mostly the same as the var keyword, but is generally preferred because of some edge cases in which let is superior.
Fusing strings and variables using the plus operator is generally considered bad practice. A better way to do the string concatenation would have been
result += `arr[${i}] = ${arr[i]}</p><p>`

How do I save code using loops while accessing json data?

I recently started my first programming project.
I started noticing that I use the same code over and over again, and would like to save that.
Here is an example:
document.getElementById('Serie1').textContent = showtrend.tv_results[0].title ;
document.getElementById('Serie1ID').textContent = "https://www.imdb.com/title/"+showtrend.tv_results[0].imdb_id ;
document.getElementById('Serie1Year').textContent = showtrend.tv_results[0].year ;
document.getElementById('Serie2').textContent = showtrend.tv_results[1].title ;
document.getElementById('Serie2ID').textContent = "https://www.imdb.com/title/"+showtrend.tv_results[1].imdb_id ;
document.getElementById('Serie2Year').textContent = showtrend.tv_results[1].year ;
I am basically adding the values I get in form of a json from my api to my site.
But how can I put all of this in a loop? It is like that for another 10 series, ant isn't very elegant
Would really appreciate the help
I think this might help you.
for (let i = 1; i < showtrend.tv_results.length; i++) {
var name = 'Serie'+ i;
document.getElementById(name).textContent = showtrend.tv_results[i-1].title ;
document.getElementById(name+'ID').textContent = "https://www.imdb.com/title/"+showtrend.tv_results[i-1].imdb_id ;
document.getElementById(name+'Year').textContent = showtrend.tv_results[i-1].year ;
}
How I would do:
ECMAScript
const BASE_URL = "https://www.imdb.com/title/";
showtrend.tv_results.forEach(function (result, i) {
let count = i + 1;
document.getElementById(`Serie${count}`).textContent = result.title;
document.getElementById(`Serie${count}ID`).textContent = BASE_URL + result.imdb_id;
document.getElementById(`Serie${count}Year`).textContent = result.year;
});
Vanilla JS
const BASE_URL = "https://www.imdb.com/title/";
for(var i = 1; i <= showtrend.tv_results.length; i++) {
document.getElementById('Serie' + i).textContent = result.title;
document.getElementById('Serie' + i + 'ID').textContent = BASE_URL + result.imdb_id;
document.getElementById('Serie' + i + 'Year').textContent = result.year;
});
In addition, maybe as your next challenge, try to get rid of these hard-coded elements in the list of "series" and make it dynamically created by pushing elements to an array in Javascript and populating a table or a list reading from this array. This will keep your code even more elegant.
Good luck with your studies!
Instead of having predefined elements in your page that you have to fill, have one container element, and then iterate over the tv_results array and compile the information from each object into divs, or a table, and then insert that HTML as the innerHTML of the container.
This method will allow you to have as many movies in the data as you need.
const json = '{"showtrend": {"tv_results": [{ "title": "Batman", "imdb_id": 1 },{ "title": "Ratman", "imdb_id": 2 }]}}';
const data = JSON.parse(json);
// Pass in the parsed data
function getHTML(data) {
// `map` over the tv_results array
return data.showtrend.tv_results.map(obj => {
// For each object in the iteration return a string of HTML.
// This method uses a template literal, and adds
// a data attribute to the outer div to identify the movie.
return (
`<div data-id="${obj.imdb_id}" class="movie">
<div>${obj.title}</div>
<div>${obj.imdb_id}</div>
</div>`
);
// Finally join the array that `map` returns
// and return that string
}).join('');
}
// Cache the container element
const container = document.querySelector('#container');
// Call `getHTML` and add the returned HTML string
// to the `innerHTML` of the container
container.innerHTML = getHTML(data);
.movie { margin-bottom: 0.5em; background-color: #efefef; padding: 0.2em; }
<div id="container"></div>
Additional documentation
querySelector
Template/string literals
map
join
Data attributes

JavaScript create appropriate number of rows and columns based off of list length

I've been struggling with converting the following C# code into something I can use in JavaScript:
var g = Model.List.GroupBy(r => Model.List.IndexOf(r) / 3).ToList();
It's use was to create the appropriate number of rows, with the appropriate number of columns within them. So for example if the list had 6 elements it would allow me to create 3 rows with 2 columns in it, this was all done in razor pages using the above GroupBy and the below code:
foreach (var parent in g)
{
#Html.Raw("<div class='row'>");
foreach (var item in parent)
{
// populate contents of row
}
#Html.Raw("</div>");
}
However for certain reasons I can't do this in Razor and need to create an alternative in JavaScript but I'm struggling to figure out a way to do this.
Primarily because I don't understand entirely how 'GroupBy' creates the list of groups and what would be a suitable alternative.
Any help, or pointing in the right direction would be great. I've tried a few solutions I found online for creating 'GroupBys' but I couldn't get them to work the way I was expecting. I also thought maybe I could split the original list into a list of dictionaries, but again had little success. I'm possibly missing something obvious.
In the end it turns out I was just missing the obvious answer, I found this excellent SO answer. I had looked at slice but couldn't quite visualise how to use it for my problem (obviously been a long day).
The post showed this snippet:
var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
temparray = array.slice(i,i+chunk);
// do whatever
}
In the end my JavaScript code looked something like this:
var listdata = await octokit.repos.listForUser({ "username": "", "type": "owner" });
var chunk = 2;
var loop = 0;
var tempArray = [];
for (var s = 0; s < listdata.data.length; s += chunk) {
tempArray[loop] = listdata.data.slice(s, s + chunk);
loop++;
}
var htmlString = "";
for (var t = 0; t < tempArray.length; t++) {
htmlString += "<div class='row'>";
var innerArray = tempArray[t];
for (var r = 0; r < innerArray.length; r++) {
var repo = innerArray[r];
htmlString +=
"<div class=\"col-md-6 col-sm-6 col-xs-12\">" +
"<div>" + repocontent + "</div>" +
"</div>"
}
htmlString += "</div>";
}
So with a list that's 6 items long, it gets split into an array that contains 3 lists of 2 items. Then I just create the html string using two for loops to create the outer bootstrap rows and the inner column classes. There's probably a more efficient way to do this but this worked a treat.

Using quotes within a javascript array and getting the result verbatim

So I have an array that looks like:
var typeArray = [
"'<span class="class">Text</span>'",
"test",
]
and im calling the array in code that looks like this:
<script>
var elem = document.getElementById("pHolderGen1");
var num = 151;
for(var i = 1; i <= num; i++) {
var d = document.createElement('div');
d.setAttribute('class', 'pfl');
d.innerHTML = '<p>'
+ typeArray[i-1] +
'</p>';
elem.appendChild(d);
}
</script>
Now I know there is an issue in the array with my use of quotes around class; however when I change it to:
var typeArray = [
"'<span class="/"grass"/">Grass</span>'",
"test",
]
as an attempt to get rid of the qoute issue I'm having all it displays is "NaN."
When I actually run the code I want it to produce (in html):
<p><span class="class">Text</span></p>
So in short I'm looking for a way to fix this and I also want to know why it does this.
Change "'<span class="class">Text</span>'" to "<span class='class'>Text</span>"

Dynamic Javascript Table

Does anybody know how to convert my data to table by using Javascript or JQuery, I got this data for example:
var data1 = ["1","3","5"]
var data2 = ["a","b","c"]
Once the data1 and data2 value is updated then the rows and columns are also automatic updated.
result:
etc:
No. Alphabet
1 a
3 b
5 c
In your .html file
<table class="myTable"></table>
In your .js file
function generateTable(data1, data2){
var $table = $('.myTable');
for (var i = 0; i < data1.length; i++){
var $aSingleContent = '<tr><td>'+data1[i]+'</td><td>'+data2[i]+'</td></tr>';
$table.append($aSingleContent);
}
}
function modifyData() {
var data1 = ["1","3","5"];
var data2 = ["a","b","c"];
generateTable(data1, data2);
}
--------------------------------Update-------
I put the code in jsfiddle
http://jsfiddle.net/ronansmith/daLf9t85/
remember to add External Resources of jQuery
I like the idea of creating a function to dynamically set the innerHTML of a div, propagating it with a table.
It should look something like this:
function generate_table(array1, array2) {
var html = '<table>';
// cycles through all the elements in the array
for (var i = 0; i < array1.length; i++) {
html += '<tr><td>' + array1[i] + '</td><td>' + array2[i] + '</td></tr>';
}
html += '</table>';
// places the table in the element
document.getElementById('content').innerHTML = html;
}
generate_table() takes in the two arrays and constructs a String, propagating it with the contents of the array in a for loop. Then it gets a div named content and sets its innerHTML to create the table.
In your webpage, be sure to include the div to which the table will be inserted:
<div id="content"></div>
You can also call generate_table() every time your array refreshes, and the table will update.
I hope this helps you!
there are two ways you can do this. one, you wrap a function/class around your data and have a "changedata" method that changes the data and updates the html or you have a setInterval that listens for changes to those variables and serves the changes to you markup. the former is much better than the latter in my opinion

Categories