Handling large json array on the browser to create tables - javascript

I am currently working on an inventory management web app where I load a large amount of data on an HTML table by using fetch with async.
async function getNewSummary() {
try {
let groups = await fetch(
"http://localhost:3000/dashboard/summary/groups", {
method: "GET"
}
);
let groupData = await groups.json();
console.log(json);
var table = document.getElementById("grouptable");
for (data in groupData)
table.innerHTML = `<tr>
<td>${groupData[data].groups}</td>
<td>${groupData[data].permission}</td>
</tr>`;
} catch (e) {
console.log(e);
}
}
My Express API returns a large JSON array which I loop through and create table rows from. I expected the browser to stop working when it does that. Is there any other way I can get it done?
I need to create the table and then use it as a DataTable.

Just curious, but I tried loading a pretty large dataset (28795 JSON objects) using your code as an example and it loaded pretty quickly. I adjusted the DOM manipulation to use something besides innerHTML, but other than that it is pretty much the same. Here is the request I made:
main.js
async function getNewSummary() {
try {
const groups = await fetch("https://raw.githubusercontent.com/prust/wikipedia-movie-data/master/movies.json");
const groupData = await groups.json();
console.log('Num documents: ', groupData.length);
console.log('Example document: ', groupData[0]);
const table = document.getElementById("grouptable");
for (data in groupData) {
let tr = document.createElement('tr');
let td = document.createElement('td');
td.textContent = groupData[data].title;
tr.appendChild(td);
table.appendChild(tr);
}
} catch (e) {
console.log(e);
}
}
getNewSummary();
And here is a gif of it working, it only takes about 2 seconds to create all 28,795 table rows. Could the problem be the way you are adding the data to the DOM and not the size of the dataset? Or is you dataset much larger? I've never worked with large amounts of data, so I could be wayyyy off base here, but wanted to try and help if I could.
Here is another version where I click a button that changes the background to red and adds some p tags while it's loading. It definitely hangs, but it's only for a second:

Here's my current iteration.
inside the browser
<script>
function inventoryWorker() {
w = new Worker("js/tableHelper.js");
w.onmessage = function (event) {
DatatableMaker(event.data);
}
}
function DatatableMaker(data) {
$(document).ready(function () {
$('#main').DataTable().destroy();
var events = $("#events");
// Setup text input
$("#main tfoot th").each(function () {
var title = $(this).text();
$(this).html(
'<input class="form-control" type="text" placeholder="Search ' + title +
'" />'
);
});
// DataTable
var table = $("#main").DataTable({
data: data,
paging: true,
lengthChange: true,
searching: true,
ordering: true,
info: true,
scrollX: true,
scrollCollapse: true,
autoWidth: true
});
}
</script>
In a seperate file tableHelper.js
async function getInventory() {
var tableRow = [];
try {
let inventory = await fetch("http://localhost:3000/dashboard/inventory", {
method: "GET"
});
let inventoryData = await inventory.json();
console.log(inventoryData);
for (data in inventoryData) {
var temp = [];
temp.push(
inventoryData[data].ReceivingDate,
// etc
);
tableRow.push(temp);
}
postMessage(tableRow);
} catch (e) {
console.log(e);
}
}
It works better but there is a slight pause when the inventoryWorker function is called

Related

Declaring elements without them being inside a function

Hi guys am having trouble with this, When I declare the elements outside the loop the script ends up being broken. I plan to use the elements in several functions. Tried a lot of times but didn't find a solution. Broke the project a few times while doing guess work. Also If there's any idea on how to shorten this or to make it work better please give me a heads up :)
async function loadTable() {
try {
while(dpTable.firstChild) dpTable.removeChild(dpTable.firstChild);
const result = await fetch('http://localhost:5000/users/student_data', { method: 'GET' });
const table = await result.json();
table.forEach((res) => {
//Create Table elements and then load results into the elements
const tr = document.createElement('tr');
const studentNo = document.createElement('td');
const name = document.createElement('td');
const surname = document.createElement('td');
const date_joined = document.createElement('td');
const fees_paid = document.createElement('td');
const fees_owing = document.createElement('td');
const is_owing = document.createElement('td');
const trash_btn = document.createElement('button');
const trash_Icon = document.createElement('i');
tr.id = 'Content';
studentNo.id = res.id;
name.id = 'fname';
surname.id = 'lname';
fees_paid.id = 'amount_paid';
fees_owing.id = 'amount_owing';
is_owing.id = 'debt';
//Enter the data from the response
studentNo.innerText = res.id;
name.innerText = res.fname;
surname.innerText = res.lname;
fees_paid.innerText = res.amount_paid;
fees_owing.innerText = res.amount_owing;
date_joined.innerText = res.date_joined;
is_owing.innerText = res.is_owing;
trash_btn.innerText = 'Delete';
//Add Classes for elements
studentNo.classList.add('trContent');
name.classList.add('trContent');
surname.classList.add('trContent');
fees_paid.classList.add('trContent');
fees_owing.classList.add('trContent');
date_joined.classList.add('trContent');
is_owing.classList.add('trContent');
trash_Icon.classList.add('fas');
trash_Icon.classList.add('fa-trash');
trash_Icon.classList.add('trash-btn');
//Append table row elements to main table
tr.append(studentNo);
tr.append(name);
tr.append(surname);
tr.append(fees_paid);
tr.append(fees_owing);
tr.append(date_joined);
tr.append(is_owing);
tr.append(trash_btn);
//Event Listeners
trash_btn.addEventListener('click', async (e) => {
if (window.confirm('Delete')) {
console.log('trash icon clicked');
const jsonReq = {};
jsonReq.id = res.id;
const result = await fetch('http://localhost:5000/users/student_data', {
method: 'DELETE',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(jsonReq),
});
alert('Deleted Record');
window.location.reload();
}
});
//Append the table as a child to the main element
dpTable.appendChild(tr);
});
} catch (e) {
console.log(`Cant load List ${e}`);
}
}
There are a couple of minor issues you need to remedy or perhaps even ignore
Element IDs must be unique so you can't use the same id for multiple cells. You could switch to using classNames instead
The buttons need to be inserted into cells, they can't be directly insterted into a row
You can cut this all down quite a bit by using an array of field names to iterate and using TableElement.insertRow() and TableRow.insertCell()
Something like:
const fields = ['studentNo','fname','lname','amount_paid','amount_owing','date_joined','is_owing'] ;
table.forEach((res) => {
const row = dpTable.insertRow();
// loop over fields array to create cell for each
fields.forEach((f) => {
const cell = row.insertCell();
cell.innerText = res[f];
cell.className = 'trContent';
});
const btnCell = row.insertCell();// cell for button
const trash_btn = document.createElement('button');
const trash_Icon = document.createElement('i');
trash_Icon.classList.add('fas', 'fa-trash', 'trash-btn');
trash_btn.append(trash_icon);
btnCell.append(trash_btn);// insert button in own cell
trash_btn.addEventListener('click', .....);
});
It is likely because you're declaring them as const which means they shouldn't be rewritten - but you have them in a loop which reassigns them and so causes an error. You can use let instead in this case
async function loadTable() {
try {
while(dpTable.firstChild) dpTable.removeChild(dpTable.firstChild);
const result = await fetch('http://localhost:5000/users/student_data', { method: 'GET' });
const table = await result.json();
let tr, studentNo, name, surname, date_joined, fees_paid, fees_owing, is_owing, trash_btn, trash_Icon
table.forEach((res) => {
//Create Table elements and then load results into the elements
tr = document.createElement('tr');
studentNo = document.createElement('td');
name = document.createElement('td');
surname = document.createElement('td');
date_joined = document.createElement('td');
fees_paid = document.createElement('td');
fees_owing = document.createElement('td');
is_owing = document.createElement('td');
trash_btn = document.createElement('button');
trash_Icon = document.createElement('i');
tr.id = 'Content';
studentNo.id = res.id;
name.id = 'fname';
surname.id = 'lname';
fees_paid.id = 'amount_paid';
fees_owing.id = 'amount_owing';
is_owing.id = 'debt';
//Enter the data from the response
studentNo.innerText = res.id;
name.innerText = res.fname;
surname.innerText = res.lname;
fees_paid.innerText = res.amount_paid;
fees_owing.innerText = res.amount_owing;
date_joined.innerText = res.date_joined;
is_owing.innerText = res.is_owing;
trash_btn.innerText = 'Delete';
//Add Classes for elements
studentNo.classList.add('trContent');
name.classList.add('trContent');
surname.classList.add('trContent');
fees_paid.classList.add('trContent');
fees_owing.classList.add('trContent');
date_joined.classList.add('trContent');
is_owing.classList.add('trContent');
trash_Icon.classList.add('fas');
trash_Icon.classList.add('fa-trash');
trash_Icon.classList.add('trash-btn');
//Append table row elements to main table
tr.append(studentNo);
tr.append(name);
tr.append(surname);
tr.append(fees_paid);
tr.append(fees_owing);
tr.append(date_joined);
tr.append(is_owing);
tr.append(trash_btn);
//Event Listeners
trash_btn.addEventListener('click', async (e) => {
if (window.confirm('Delete')) {
console.log('trash icon clicked');
const jsonReq = {};
jsonReq.id = res.id;
const result = await fetch('http://localhost:5000/users/student_data', {
method: 'DELETE',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(jsonReq),
});
alert('Deleted Record');
window.location.reload();
}
});
//Append the table as a child to the main element
dpTable.appendChild(tr);
});
} catch (e) {
console.log(`Cant load List ${e}`);
}
}
You can definitely simplify your code and make it more flexible into the process. The JSON data returned will have a set of keys - these can be used to build the HTML output without knowing, in advance, what they are. The beauty of that is the same code can display greatly different datasets very easily.
If you were to use the method create shown below to create the new DOM elements you greatly reduce the code needed.
<?php
#---------------------------------------------------
# to emulate whatever "users/student_data" returns.
# this is dummy data for example only.
#
set_include_path('c:/wwwroot/dbo/');
require 'db-conn-details.php';
require 'mysqli-conn.php';
/*
When we receive the AJAX request, identified by querystring,
prepare some sql statement and execute. Return the full
recordset in JSON format.
*/
if( isset( $_GET['fetch'] ) && $_GET['fetch']=='data' ){
ob_clean();
$sql='select * from `sport` limit 10';
$res=$db->query( $sql );
$json=json_encode( $res->fetch_all( MYSQLI_ASSOC ) );
exit( $json );
}
if( $_SERVER['REQUEST_METHOD']=='DELETE' ){
exit('DELETE');
}
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title></title>
<style>
#data{ border:1px solid grey;display:table;padding:1rem;width:100%; }
</style>
<script>
// The endpoint used by the ajax query - in this example it is same page.
const url=location.href; //http://localhost:5000/users/student_data
const create=function(t,a,p){
/*
utility to add a new node with attributes and content.
t=type ~ the DOM node type, defaults to DIV
a=attrbutes to set
p=parent to append new node to.
*/
let el=(typeof(t)=='undefined'||t==null)?document.createElement('div'):document.createElement(t);
let _arr=['innerHTML','innerText','html','text'];
for(let x in a)if(a.hasOwnProperty(x)&&!~_arr.indexOf(x))el.setAttribute(x,a[x]);
if(a.hasOwnProperty('innerHTML')||a.hasOwnProperty('html'))el.innerHTML=a.innerHTML||a.html;
if(a.hasOwnProperty('innerText')||a.hasOwnProperty('text'))el.innerText=a.innerText||a.text;
if(p!=null )typeof(p)=='object'?p.appendChild(el):document.getElementById(p).appendChild(el);
return el;
};
async function loadTable() {
try {
// It was not clear where dbTable was defined - hence declared here
const dpTable=document.querySelector('table#data');
while( dpTable.firstChild ) dpTable.removeChild( dpTable.firstChild );
// As this example targets the same page I needed a way to return only db results - hence using querystring.
const result = await fetch( url + '?fetch=data' );
const json = await result.json();
// the callback that will process the button clicks. The button has a dataset attribute set
// that will use the res.id value from each JSON record.
const clickhandler=async function(e){
if( window.confirm('Delete') ) {
const jsonReq = {
id:this.dataset.id
};
const result = await fetch( url, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify( jsonReq ),
});
console.log( result );
alert('Record deleted');
window.location.reload();
}
};
json.forEach( res => {
// find all the keys from the JSON ~ one of which will be the ID for the record ( res.id )
let fields=Object.keys( res );
// generate new table row
let tr=dpTable.insertRow();
// iterate through all json keys - add new table cell with JSON data.
fields.forEach( field => {
create('td',{
'data-id':field,
text:res[field]
},tr);
});
// add the button for the row and assign listener - also assign the dataset attribute for the record ID.
let bttn=create('button',{ text:'Delete','data-id':res.id }, tr.insertCell() );
bttn.addEventListener( 'click', clickhandler );
// add the icon to the button
create('i',{ 'class':'fas fa-trash trash-btn' }, bttn );
});
} catch(e) {
console.log('Unable to load list: %o',e);
}
}
document.addEventListener('DOMContentLoaded',loadTable);
</script>
</head>
<body>
<table id='data'></table>
</body>
</html>
The sql query shown here in the demo returns the following data:
[
{"id":"1","tag":"3D Archery","description":"a form of archery in which the goal is to strike targets in the shape of animals at unknown distances.","enabled":"1"},
{"id":"2","tag":"3x3","description":"a variation of basketball played on a half court with just three players in each team.","enabled":"1"},
{"id":"3","tag":"Abseiling","description":"an adventure sport where the participants descend a steep formation using a rope. (not really a sport).","enabled":"1"},
{"id":"4","tag":"Acroski","description":"athletes on snow skis perform various choreographed\u00c2\u00a0routines (once called Ski Ballet)","enabled":"1"},
{"id":"5","tag":"Adventure Racing","description":"an event combining two or more endurance disciplines, such as Orienteering, Cross-Country Running, Mountain Biking, Paddling and Climbing. It is also called Expedition Racing.","enabled":"1"},
{"id":"6","tag":"Aerials","description":"a freestyle skiing discipline in which athletes ski along a take-off ramp, then perform various in-air tricks.","enabled":"1"},
{"id":"7","tag":"Aerobatics","description":"sport aerobatics involves aircraft maneuvers such as rolls, loops, stall turns (hammerheads), and tailslides.","enabled":"1"},
{"id":"8","tag":"Acrobatic Gymnastics","description":"team of gymnasts work together to perform acrobatic moves in combination with dance moves.","enabled":"1"},
{"id":"9","tag":"Aerobic Gymnastics","description":"another name for Sport Aerobics.","enabled":"1"},{"id":"10","tag":"Aeromodeling","description":"activity using remotely controlled flying model aircraft (not really a sport).","enabled":"1"}
]
And yielded the following output:

How to call for JSON output from an API into HTML?

new to building web apps. please advise on some resources to read up if you have any good ones!
Problem:
I have created a call API on AWS - https://tnmw5vn267.execute-api.us-east-1.amazonaws.com/dev
the output is a JSON object.
However, I have no clue how to put this into a HTML page (i.e. How to get the JSON object into the HTML and then subsequently show it as a table), only:
`function CreateTableFromJSON() {
var myEmployees = [
{
"FirstName": "Benjamin",
"LastName": "Tan"
}
]
// EXTRACT VALUE FOR HTML HEADER.
// ('Book ID', 'Book Name', 'Category' and 'Price')
var col = [];
for (var i = 0; i < myEmployees.length; i++) {
for (var key in myEmployees[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
// CREATE DYNAMIC TABLE.
var table = document.createElement("table");
// CREATE HTML TABLE HEADER ROW USING THE EXTRACTED HEADERS ABOVE.
var tr = table.insertRow(-1); // TABLE ROW.
for (var i = 0; i < col.length; i++) {
var th = document.createElement("th"); // TABLE HEADER.
th.innerHTML = col[i];
tr.appendChild(th);
}
// ADD JSON DATA TO THE TABLE AS ROWS.
for (var i = 0; i < myEmployees.length; i++) {
tr = table.insertRow(-1);
for (var j = 0; j < col.length; j++) {
var tabCell = tr.insertCell(-1);
tabCell.innerHTML = myBooks[i][col[j]];
}
}
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
}`
The above only generates a static table that doesn't update based on my API output. Am not sure how to do it?
Let me show you the solution.
Firstly you need to fetch JSON from API by fetch function.
After that, you need to put it to a particular HTML element by .innerHTML
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
</head>
<body>
<script>
async function fetchDataAndRenderTable() {
const serverURL = 'https://tnmw5vn267.execute-api.us-east-1.amazonaws.com/dev'
const dataFromTheServer = await fetch(serverURL).then(res => res.json())
const tableHTMLElement = document.createElement('table')
tableHTMLElement.innerHTML = `
<table style="width:100%">
<tr>
<th>FirstName</th>
<th>LastName</th>
</tr>
${
dataFromTheServer.map(item => {
return `
<tr>
<td>${item.FirstName}</td>
<td>${item.LastName}</td>
</tr>
`
})
}
</table>
`
document.body.appendChild(tableHTMLElement)
}
fetchDataAndRenderTable()
</script>
</body>
</html>
PS. But your API needs to allow CORS, otherwise you will be not able to fetch it from the browser
Here's how I might approach it using plain JS.
Use fetch to get the data from the API.
Use template literals to build up a sequence of HTML strings using map and join.
Add the final string to the page.
const json = '[{"LatestGreetingTime":"2021-06-19T15:47:10.539Z","ID":"Hello from Lambda, Benjamin Tan","FirstName":"Benjamin","LastName":"Tan"},{"LatestGreetingTime":"2021-06-19T13:44:33.761Z","ID":"Hello from Lambda, ichal shodin","FirstName":"ichal","LastName":"shodin"},{"LatestGreetingTime":"2021-06-19T13:44:33.761Z","ID":"Hello from Lambda, victoria Lovelace","FirstName":"victoria","LastName":"Lovelace"}]';
// This simulates a call to your API
function mockFetch() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 1000);
});
}
// Grab the button, and a div where you can place the table
const button = document.querySelector('button');
const div = document.querySelector('div');
// Add an event listener to your button that
// callApi when it's clicked
button.addEventListener('click', callApi, false);
// As mentioned the comments you should be using
// the fetch API here, and it would look like
// fetch(url).then(res => res.json).then(buildTable)
function callApi() {
mockFetch()
.then(res => JSON.parse(res))
.then(buildTable);
}
// This function takes the data, extracts the headings
// from the first object, and then builds the row data.
// It finally puts everything together as a template literal
// and adds it to the div
function buildTable(data) {
const headings = buildHeadings(Object.keys(data[0]));
const rows = buildRows(data);
div.innerHTML = `<table><thead>${headings}</thead><tbody>${rows}</tbody></table>`;
}
// Iterate over the headings array and return heading HTML
function buildHeadings(headings) {
return headings.map(heading => `<th>${heading}</th>`).join('');
}
// Iterate over the data return row HTML
function buildRows(data) {
return data.map(row => `<tr>${buildCells(row)}</tr>`).join('');
}
// Iterate over the row data and return cell HTML
function buildCells(row) {
return Object.values(row).map(cell => `<td>${cell}</td>`).join('');
}
table { border-collapse: collapse; padding 0.2em; }
td { padding: 0.5em }
<button>Call API</button>
<div />

jQuery Append only works in debug in Mozilla Firefox

I am using SignalR for Chat in my app. When a user connects, I use .append to add the users name to a div with a list of users. The code works fine in Chrome and Edge, but when I run it in FireFox the append does not work unless I hit F12 and run the code in debug mode.
Here is my javascript:
$(document).ready(function () {
initLoad = true;
loadRequests();
#Html.Raw(tabstr)
// Declare a proxy to reference the hub.
var chatHub = $.connection.chatHub;
$.connection.hub.start().done(function () {
registerEvents(chatHub)
});
registerClientMethods(chatHub);
});
function registerClientMethods(chatHub) {
chatHub.client.onNewUserConnected = function (connectionId, name, jobtitle) {
AddUser(chatHub, connectionId, name, jobtitle);
}
}
function AddUser(chatHub, connectionId, name, jobtitle) {
var userId = $('#hdId').val();
const connectionID = connectionId;
const userName = name;
const jobTitle = jobtitle;
const connectedUser = $("#divusers").append('<div id="' + connectionID + '" class="kt-widget__item" style="cursor:pointer"><div class="kt-widget__info"><div class="kt-widget__section"><a class="kt-widget__username">' + userName + '</a><span class="kt-badge kt-badge--success kt-badge--dot"></span></div>' + jobTitle + '</div></div>');
connectedUser.on("click", function () {
var groupWindows = [];
$('#groupWindowList').empty();
$('div[id^="ctr"]').each(function () { groupWindows.push(this.id); })
$.each(groupWindows, function (index, value) {
var controlname = value;
const groupName = controlname.replace("ctr", "")
const listItem = $(`<li>${groupName}</li>`);
listItem.on("click", function () {
$('#addToGroupModal').modal('toggle');
chatHub.server.addUserToGroup(groupName, connectionID);
});
$('#groupWindowList').append(listItem);
})
$('#addToGroupModal').modal({});
});
}
const connectedUser + $("#divusers").append is the problem. In debug, it is fine, but if I just run the code, the append does not take place and user's name does not display in the list.
Here is my html:
<div class="kt-portlet__body">
<div class="kt-widget kt-widget--users kt-mt-20">
<div class="kt-scroll kt-scroll--pull">
<div id="divusers" class="kt-widget__items">
</div>
</div>
</div>
</div>
UPDATE
So I added:
var myElement = $('#divusers')[0];
var observer = new MutationObserver(function(mutations) {
if (document.contains(myElement)) {
alert('hi');
});
observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});
to check if the element exists in the DOM. I replaced the alert('hi') with the code to start the ChatHub and the append still does not work.
Another weird thing is if I make almost ANY change in the html and run it. It works the first time, but if I stop it and run it again. It doesn't work.
Any assistance is greatly appreciated.

Real time Javascript filtering not working throws undefined [duplicate]

This question already exists:
My live string search always returns false when it tries to match with includes() [duplicate]
Closed 3 years ago.
I want the code to return items from the JSON when i write something in the search bar (getting data from JSON tested and working).
The code is supposed to go to the JSON, read the line of the JSON (object) and compare with the keyword to see if it contains the keyword. If it contains it will display in <li> the item. It is always throwing "undefined".
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet">
</head>
<body>
<input type="text" placeholder="Search Users" id="filter_items"/>
<ul id="items-list">
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
var json = (function() {
var json = null;
$.ajax({
'async': false,
'global': false,
'crossDomain': true,
'method': "get",
'url': "products.json",
'dataType': "json",
'success': function (data) {
json = data;
}
});
return json;
})();
// lets filters it
ul = document.getElementById("items-list");
input = document.getElementById('filter_items');
var filterItems = function(event, json){
keyword = input.value.toLowerCase();
var li = "";
for (var index in json)
{
for (var j in json[index])
{
var line = json[index][j];
var filtered_items = line.title.filter(function(lin){
lin = lin.title.toLowerCase();
return lin.title.indexOf(keyword) > -1;
});
}
}
li += "<li>"+filtered_items+"</li>";
ul.innerHTML = li;
}
input.addEventListener('keyup', filterItems);
</script>
</body>
</html>
The JSON
{
"items": [{
"title": "Express"
}, {
"title": "Unexpress"
}]
}
It is supposed to return items matching the keyword in real time.
UPDATE
JSON isn't passing as parameter into the function with event as parameter too. Anyone know how to solve it?
You aren't passing an additional variable to the function when you trigger the event, so it's unclear why you'd expect json to be populated.
But you don't need it anyway - the content of JSON doesn't change every time the event happens, the data is always the same. Just download it when your page loads, and then add the event listener when it's finished downloading, and use the function without the additional parameter. json can be declared global so it'll be in scope.
I think the code might make more sense like this:
var json = null;
$.ajax({
'global': false,
'crossDomain': true,
'method': "get",
'url': "products.json",
'dataType': "json",
'success': function (data) {
json = data;
input.addEventListener('keyup', filterItems);
}
});
ul = document.getElementById("items-list");
input = document.getElementById('filter_items');
var filterItems = function(event){
keyword = input.value.toLowerCase();
var li = "";
for (var index in json)
{
for (var j in json[index])
{
var line = json[index][j];
var filtered_items = line.title.filter(function(lin){
lin = lin.title.toLowerCase();
return lin.title.indexOf(keyword) > -1;
});
}
}
li += "<li>"+filtered_items+"</li>";
ul.innerHTML = li;
}
P.S. 'async': false is deprecated due to the poor user experience it creates (locking up the main browser UI during requests). Some browsers will issue a console warning when you try to use it. But there should be no need for it. You should aim to use callbacks/promises correctly instead. I removed it in my example above, and instead (for reliability) we don't add the event listener to the input box until the JSON download has completed. Hopefully it isn't too big a file.
P.P.S. If you ever find yourself swapping the static JSON file for a server-side script to retrive product data from a database, it would be advisable to change your code so that the filtering of data happens on the server - usually it's a lot more efficient to filter using a SQL query and return only the data which is truly needed, than to download everything and then filter it using JavaScript.
I belive the filtering part should be changed.
Maybe to something like the following?
var json = { "items": [{"title": "Express"}, { "title": "Unexpress"}] };
// lets filters it
ul = document.getElementById("items-list");
input = document.getElementById('filter_items');
var filterItems = (event) => {
keyword = input.value.toLowerCase();
var li = "";
var filtered_items = json.items.filter(function(lin){
return lin.title.toLowerCase() == keyword;
});
for ( var i in filtered_items ) {
li += "<li>"+filtered_items[i].title+"</li>";
}
ul.innerHTML = li;
}
input.addEventListener('keyup', filterItems);
<body>
<input type="text" placeholder="Search Users" id="filter_items"/>
<ul id="items-list">
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

SQLite access in Javascript

I want to access Sql Lite Database with JavaScript code. The JavaScript code is used in html5 and has to be deployed on blackberry 10 platform.
I use the following code without success:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
<title>Prova</title>
</head>
<body>
<script type="text/javascript">
//Provenia SRL ITC - Paola Savioli
//Questa funzione apre il database SQL Lite
//Il parametro che va cambiato รจ il nome del database
function ApriDatabase() {
try {
if (window.openDatabase) {
var shortName = 'Ristoranti.sqllite';
var version = '1.0';
var displayName = 'Ristoranti italia';
var maxSize = 65536; // in bytes
db = openDatabase(shortName, version, displayName, maxSize);
}
} catch (e) {
alert('Apri Database' + e);
}
}
//Provenia SRL ITC - Paola Savioli
// Questa funzione eseque una query su un database aperto con la funzione ApriDatabase
function EseguiQuery($query, callback) {
try {
ApriDatabase();
if (window.openDatabase) {
db.transaction(
function (tx) {
tx.executeSql($query, [], function (tx, result) {
if (typeof (callback) == "function") {
callback(result);
} else {
if (callback != undefined) {
eval(callback + "(result)");
}
}
}, function (tx, error) {});
});
return rslt;
}
} catch (e) {
alert('Esegui Query' + e);
}
}
function VisualizzaComuni() {
try {
var schemanode = document.GetElementById('RCOMUNI');
schemanode.innerHTML = "";
var result = EseguiQuery('SELECT * FROM COMUNE');
for (var i = 0; i < result.rows.lenght; ++i) {
var row = result.row.item(i);
var notediv = document.createElement('div');
notediv.innerHTML = 'Codice Provincia:' + row['PROVINCIA'] + 'Nome:' + row['NAME'];
schemanode.appendchild(notediv);
}
} catch (e) {
alert('Visualizza Comuni' + e);
}
}
</script>
<input type="button" name='select' onClick="VisualizzaComuni()"
value='Visualizza Comuni'>
<div id="RCOMUNI"></div>
</body>
</html>
You may consider using the WebSQL API, which is supported on BlackBerry 7 and 10 according to http://caniuse.com/#feat=sql-storage.
Note that the API will never become an official standard and development on it has stopped. But if you just want to target BlackBerry, it may be a valid choice.
There were a few problems with the code you've posted, including a reference to .lenght instead of .length and use of try catch blocks when there are success and error handlers built-in. So I worked up a demo.
First, it does not seem to make a difference, but this is HTML5 right? Instead of an HTML 4.01 Transitional doctype, use the HTML5 doctype:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>demo by userdude</title>
...
Next, I modified the markup for the purposes of the demonstration. In this case, we have:
<body>
<input type="button" id="run" value='Run Query'>
<div id="query"></div>
<table id="table" border="1" cellspacing="1" cellpadding="5"></table>
</body>
</html>
In the head element, I use an event listener to wait for the DOM to load. Keep in mind, I do not have a Blackberry to test this with, and with Blackberry or other devices, you should probably use deviceready instead of load. I think. I also attach the event handler for button that run's the query using .addEventListener, but notice I do that within the load handler. You have to wait before trying to access the DOM.
Also, IE supports attachEvent instead of addEventListener. I would imagine Blackberry supports the latter, but I'm not sure.
window.addEventListener('load', function load(){
var run = document.getElementById('run'),
data = document.getElementById('table'),
qtext = document.getElementById('query'),
dropped = false,
created = false,
cities = ['Houston', 'Dallas', 'Paris', 'New York', 'Buenos Aires', 'London'],
shortName = 'Cities',
version = '1.0',
displayName = 'Cities Demo',
maxSize = 5 * 1024 * 1024,
db = false,
queries = [];
run.addEventListener('click', query);
This establishes my database, including running the initial call to populate() so we have some data use.
open();
This is the function I added to the run button.
function query() {
transact('SELECT * FROM Cities', view);
}
This is just meant to add data to the database. See the cities variable above.
function populate(tx) {
var city,
i = 0;
I block this from running once I've emptied the cities array of entries to INSERT. dropped and created do the same thing for the DROP and CREATE transactions.
Take special note how I'm doing this; see the transact('...', populate)? I use populate in this situation to loop back until I've finished adding all of the cities entries. This is asynchronous, so you have to setup the callbacks to wait if necessary for the previous queries to run. In this case, I could end up dropping the table after adding my rows. So I have to wait, then loop through the cities list.
if (cities) {
if (!dropped) {
dropped = true;
transact('DROP TABLE IF EXISTS Cities', populate);
return;
}
if (!created) {
created = true;
transact('CREATE TABLE IF NOT EXISTS Cities (id unique, City)', populate);
return;
}
I don't need to iterate back to populate here, since I just need to INSERT and move on.
while (city = cities.pop()) {
transact('INSERT INTO Cities (id, City) VALUES (' + i++ + ', "' + city + '")');
}
cities = false;
}
}
All this function does is give either an opened or new reference to the database, or return false. This short-circuits the execution of transact().
function open() {
if (!db && window.openDatabase) {
db = window.openDatabase(shortName, version, displayName, maxSize);
}
if (cities) {
db.transaction(populate);
}
return db;
}
This is the meat of the script. I call it from query(), and the callback in this case is view, which points to the function which runs through the result set and creates a table from the set.
function transact(query, callback) {
var cb = callback,
qel = document.createElement('p'),
qid = queries.length;
if (!open()) {
console.log('HTML5 Database not supported.');
return false;
}
db.transaction(transact_cb);
qel.innerHTML = query + ' Query Result: <span id="q' + qid + '">Pending...</span>';
qtext.appendChild(qel);
queries[qid] = query;
Note the last two arguments, transact_success, transact_error. This is how you handle these asynchronous calls.
function transact_cb(tx) {
tx.executeSql(query, [], transact_success, transact_error);
}
Not quite sure why there's an eval in there...?
function transact_success(tx, result) {
var rtext = document.getElementById('q' + qid);
rtext.className = 'success';
rtext.innerHTML = 'Success.';
if (typeof cb == "function") {
cb(result);
} else if (cb != undefined) {
eval(cb + "(result)");
}
}
Note the console.log(error);.
function transact_error(tx, error) {
var rtext = document.getElementById('q' + qid);
rtext.className = 'error';
rtext.innerHTML = 'Error logged to console.';
console.log(error);
}
}
And this function creates the table result set view. You'll probably notice I loop through each row, and each row's columns.
function view(result) {
var thead = '<thead><tr>',
tbody = '<tbody>',
row,
col;
for (var i = 0, rows = result.rows.length; i < rows; ++i) {
row = result.rows.item(i);
tbody += '<tr>';
for (col in row) {
if (i === 0) {
thead += "<th>" + col + "</th>";
}
tbody += '<td>' + row[col] + '</td>';
}
tbody += '</tr>';
}
thead += '</tr></thead>';
tbody += '</tbody>';
data.innerHTML = thead + tbody;
}
});
You can download the file and run it locally (due to a security error, it won't run on jsFiddle) by downloading the HTML file here:
http://pastebin.com/FcSiu6ZZ
So there you go. Hopefully that will help make this easier to understand. Let me know if you have any questions.

Categories