I have an inventory item search tool that filters through the huge json file (about 8mb) and if item was found it simply displays all the information regarding that item in a card format.
So now when I load the page, the caching of the file that is 8mb only begins once I start to input characters in the input box of my page.
How can I change this behavior and have the file cached upon loading the page as well as pass the json object to "get_item" function so its ready to match items as soon as I start inputting characters.
I tried using DOMContent.onload and various other onload triggers but no luck :(
Please help.
This is the js code:
const match_items = document.getElementById('match_items');
const get_item = async input_text => {
const inventory_file = await fetch('/static/json/inventory_cookware_2020.json');
const inventory_json = await inventory_file.json();
let item_matches = inventory_json.filter(item => {
const regex = new RegExp(`^${input_text}$`, 'gi');
return item.match(regex);
});
if (input_text.length < 10){
item_matches = [];
match_item.innerHTML = '';
}
display_item_data(item_matches);
};
function display_item_data(item_matches){
code to display item data...
}
search.addEventListener('input', () => get_item(search.value)); ```
HTML--------------------------------------
<div id="input_box">
<input type="search" id="search" placeholder="Search Inventory">
</div>
<div id="match_item"></div>
To have the fetch of the JSON file start when the page is first accessed, add the following code to the beginning. This uses an IIFE (Immediately Invoked Function Expression) to fetch and populate the data when the page first loads. The search input is disabled until the file has finished loading.
let inventory_json;
const search = document.getElementById('search');
search.addEventListener('input', () => get_item(search.value));
(async () => {
const inventory = await fetch('/static/json/inventory_cookware_2020.json');
inventory_json = await inventory_file.json();
search.disabled = false;
})();
const match_items = document.getElementById('match_items');
const get_item = input_text => {
// This ensures the function won't run until the AJAX is complete
// and inventory_json is populated
if (!is_loaded) {
return;
}
let item_matches = inventory_json.filter(item => {
const regex = new RegExp(`^${input_text}$`, 'gi');
return item.match(regex);
});
if (input_text.length < 10){
item_matches = [];
match_item.innerHTML = '';
}
display_item_data(item_matches);
};
HTML
<input type="text" id="search" placeholder="Search Inventory" disabled />
Related
I am trying to fire off multiple urls (so it opens in a new tab) from an xml file with one single click, but not sure how to do this. Not sure if this is the right approach, but is what I got so far:
<button id="openTab">
Click
</button>
<script>
const getURLs = (xml) => {
// get users
const urls = xml.getElementsByTagName("urls");
for (var i = 0; i < names.length; i++) {
console.log(names.children[0]) // list of all urls
}
};
const getResponseXML = async () => {
// get XML
// retrieve XML
const xml = await fetch("/wp-content/sitemap.xml");
const parsedXML = await xml.text();
getURLs(new window.DOMParser().parseFromString(parsedXML, "text/xml"));
};
getResponseXML();
</script>
I have 2 separate div elements and I have a function that defines each element and is supposed to only act on the element. so when i call typeField(name,element1) it should type into only element1 and similarly then I call typeField(address,element2) it should type into only element2.. but currently if you check my codepen implementation it's mixing the characters and I am not sure why.
Also after this I need to figure a way out where I can get the name to type in first and then after that's done I need to start the typing of the address. My assumption is I need to promisify the functions and then call them as async/await. Is that the right approach? or am I overthinking this?
So my 2 questions are
What am I doing wrong in my code that's mixing up the contents in elements innerHTML?
Once that's resolved how can I make the first element type first and on completion start the next one
Any help in resolving this would be appreciated.
This is the link to my Codepen
https://codepen.io/alimbolar/pen/Rwjoqbm?editors=1111
const typewriter1 = document.getElementById('typewriter1');
const typewriter2 = document.getElementById('typewriter2');
const name = 'Alim Bolar';
const address = "Some part of the world";
const currentLocation = "Bali, Indonesia";
const profile = "Frontend Development, Backend Development, WordPress Development"
let content = "";
let i = 0;
function typeField(x, element) {
if (i < x.length) {
alphabet = x.charAt(i);
content = content + alphabet;
element.innerHTML = content;
i++;
setTimeout(function() {
typeField(x, element)
}, 500);
}
}
typeField(name, typewriter1);
typeField(address, typewriter2);
<header>
<div>NAME : <span id="typewriter1"></span></div>
<div>ADDRESS : <span id="typewriter2"></span></div>
</header>
You need to
not have i or content be global, because then it'll be shared by all calls of typeField
have typeField assign to a persistent Promise outside that further calls are chained off of
Consider awaiting a Promise inside a loop, it'll look a lot cleaner than recursion.
const typewriter1 = document.getElementById('typewriter1');
const typewriter2 = document.getElementById('typewriter2');
const name = 'Alim Bolar';
const address = "Some part of the world";
const currentLocation = "Bali, Indonesia";
const profile = "Frontend Development, Backend Development, WordPress Development"
let prom = Promise.resolve();
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function typeField(textToInsert, element) {
prom = prom.then(async () => {
for (const char of textToInsert) {
element.textContent += char;
await delay(500);
}
});
}
typeField(name, typewriter1);
typeField(address, typewriter2);
<header>
<div>NAME : <span id="typewriter1"></span></div>
<div>ADDRESS : <span id="typewriter2"></span></div>
</header>
I'm learning Apps Script and I'm getting very frustrated because I can't see what is wrong with my approach.
According to the docs, when running google.script.run.withSuccessHandler(successFunc).myFunction() from within an html file script, whatever myFunction returns should be available as a parameter for successFunc, or shouldn't it? I'm dubious now because I can't seem to make it work.
Here's my HTML:
<body>
<form>
<label for="fn">Nombre</label>
<input id="fn" name="fn" type="text">
<label for="ln">Apellido/s</label>
<input id="ln" name="ln" type="text">
<button type="button" id="search">Buscar</button>
<label for="found">Resultado</label>
<input type="text" name="found" id="found" disabled>
<button type="submit" id="setClient">Continuar</button>
</form>
<script>
const fn = document.getElementById('fn').value;
const ln = document.getElementById('ln').value;
const search = document.getElementById('search');
const found = document.getElementById('found');
const setClient = document.getElementById('setClient');
let clientId; // The ID of the client in the database;
let data;
search.addEventListener("click", () => {
google.script.run.withSuccessHandler(addClient).searchDb(fn, ln);
});
function addClient(data) {
alert(data); // returns null
if (!data || data === null) throw "No data found"; // don't care about gracefully catching this right now
found.value = `${data.clientFn}, ${data.clientLn}`; // adds 'undefined, undefined'
clientId = data.clientId;
// google.script.host.close();
}
setClient.addEventListener("click", ()=>{
google.script.run.setClient(fn, ln, clientId);
})
</script>
</body>
As you can see, I've added an "alert" in the success handler just to keep track of data and even though I have tested the code ad infinitum and it always return a fully workable object, this seems to always show null.
Here's also my .gs function:
function searchDb(fn, ln) {
const ws = SpreadsheetApp.openById("the ID of the SS, obviously").getSheets()[0];
const dataRange = ws.getRange(2, 1, ws.getLastRow() - 1, 3).getValues();
const lnRange = dataRange.map(r => { return r[2] });
const fnRange = dataRange.map(r => { return r[1] });
const idRange = dataRange.map(r => { return r[0] });
for (let a = 0; a < lnRange.length; a++) {
const clientLn = lnRange[a];
const clientFn = fnRange[a];
const clientId = idRange[a];
if (clientLn.includes(ln) && clientFn === fn) {
const data = {
clientFn: clientFn,
clientLn: clientLn,
clientId: clientId
}
return data;
}
}
return;
}
It is, indeed, a very simple program and it does return the data object with the appropriate data when I test it, so not sure what's going on here.
I'm pretty sure I'm missing something pretty obvious, but I got to the point where I'm just banging my head against the wall...
Any help will be appreciated!
EDIT
I've edited the code because what I originally posted wasn't consistent - and the result of not reverting back completely from trying other approaches -. Still, the problem remains.
This is not an array:
const data = {'clientFn': clientFn,'clientLn': clientLn,'clientId': clientId}
But your treating it as an array:
found.value = `${data[1]}, ${data[0]}`;
clientId = data[2];
Try referring to them with their properties 'clientFn','clientLn','clientId'
found.value = `${data['clientLn']}, ${data['clientFn']}`;
clientId = data['clientId'];
Also you may want to refer to this link to insure that they are all legal types.
It turns out the problem was not to do with withSuccessHandler, as I expected, but with how the data was passed on to searchDb because the vars fn and ln haven't changed value as far as the script is concerned when the search button is clicked. The solution is as simple as declaring those variables after the search button has been clicked:
<script>
const search = document.getElementById('search');
const fullName = document.getElementById('fullName');
const country = document.getElementById('country');
const email = document.getElementById('email');
const noClient = document.getElementById('noClient');
const useClient = document.getElementById('useClient');
let clientId; // The ID of the client in the database;
search.addEventListener("click", () => {
const fn = document.getElementById('fn').value;
const ln = document.getElementById('ln').value;
google.script.run.withSuccessHandler(addClient).searchDb(fn, ln);
});
// ...etc
I want to rerun the search or populate the results as if the user never left.
So I visit the page, search for cats and get some results based on cats. I close the tab, come back later, and rather than find myself on a fresh page that has no results, I’m provided with the results from my last search. I am only allowed to use Javascript and local storage to achieve this. Here is my JS file:
var search = document.getElementById('searchTerm');
if(!localStorage.getItem('input')) {
populateStorage();
}
// To set the event to listen to
const form = document.querySelector('.js-search-form');
// To listen when user hits submit or press enter
form.addEventListener('submit', (e) => {
// Start of the query string
const base = `https://api.themoviedb.org/3/search/movie?include_adult=false&page=1&`;
// Extension of the query string
const searchTerm = document.querySelector('#searchTerm').value;
const language = `en-US&`;
const api_key = `7ab3cb18b4ad4a07bbd8bb01acfa7091`;
// The complete build of the url
const url = `${base}query=${searchTerm}&language=${language}api_key=${api_key}`;
// The get method
const option = {
method: 'GET'
}
// Stops the default action
e.preventDefault()
fetch(url)
//Parse data
.then( response => {
if (response.ok) {
return response.json()
} else {
throw response
}
})
// Do something with data
.then( data => {
console.log(data)
// Output results
let resultElement = '';
if(data.results.length > 1) {
resultElement += `
<h2><span>Results for ${searchTerm}</span></h2>
<section class="js-search-results clearfix">
`;
if(data.results) {
data.results.forEach(function(results){
resultElement += `<article class="item">
<div class="container">
<img src="https://image.tmdb.org/t/p/w500${results.poster_path}"/>
<div class="content">`;
if(results.title.length > 17) {
resultElement += `<h3>${results.title.substr(0,17)}...</h3>`;
} else {
resultElement += `<h3>${results.title}</h3>`;
}
resultElement += `<p>Released: ${results.release_date}</p>`;
resultElement += `</div>
</div>
</article>`;
});
resultElement += `</section>`;
populateStorage()
} else {
resultElement += '<p class="no-results">No results</p>';
}
document.querySelector('.js-search-results').innerHTML = resultElement;
}
})
.catch(err => {
console.log(err)
})
})
function populateStorage() {
localStorage.setItem('searchTerm', document.getElementById('searchTerm').value);
}
EDIT -
You are calling localStorage.getItem('input'); but you are saving the local storage item as searchTerm please make these names consistent with one another
you either need to change localStorage.getItem('input')
TO
localStorage.getItem('searchTerm');
OR
change localStorage.setItem('searchTerm', document.getElementById('searchTerm').value)
TO
localStorage.setItem('input', document.getElementById('searchTerm').value)
-Edit-
Assuming that this is wrapped in a document ready function, update your initial check of storage to look like this...
var search = document.getElementById('searchTerm');
//This assumes that you corrected your getItem to reference 'searchTerm'
if(!localStorage.getItem('searchTerm')) {
populateStorage();
}
else {
search.value = localStorage.getItem('searchTerm');
}
Your code never makes a call to actually populate that field so this should fix that.
I am building an application that will load bus schedules into local storage, then based on a search term provides the stops on the bus schedule. It works by clicking the load button and sending the information to local storage. Then you search a route name, and the stops information will be displayed into results. When I run in a browser my data is not loading into local storage when I press load.
<html>
<head>
<script type="text/javascript">
/**
* A JSON string that holds data. There is no problem with the JSON string
* #type {String}
*/
var busSchd = '{"Routes": [{"routeName": "Milledge","stops":[{"Stop 1":"Main Library","Stop 2":"Clarke Central","Stop 3":"Five Points South","Stop 4":"Five Points North","Stop 5":"Waddell"}]},{"routeName": "Orbit","stops":[{"Stop 1":"Main Library","Stop 2":"Clarke Central","Stop 3":"Five Points South","Stop 4":"Five Points North","Stop 5":"Waddell"}]},{"routeName": "East West","stops":[{"Stop 1":"East Campus Deck","Stop 2":"Creswell Hall","Stop 3":"Hull St Deck","Stop 4":"Main Library","Stop 5":"Joe Frank Harris"}]}]}';
const load = () => {
let data = JSON.parse(busSchd);
console.log("a");
for (var i = 0; i < data.Routes.len;)
{
let route = data.Routes[i];
let routeStr = route.routeName;
localStorage.set(routeStr, JSON.stringify(route.stops));
}
};
const clicked = () => {
var search = document.getElementById("search");
var results = localStorage.getItem("search");
if (results === null) {
document.getElementById("result").innerHTML = "<b>There are no results for that route name.</b>";
} else {
var stops = results;
var output = '';
for (var key in stops[0]) {
output = output + '<b>' + key + '</b> : ' + stops[0][key] + '<br>';
}
document.getElementById("result").innerHTML = output;
}
};
</script>
</head>
<body>
<input type="button" value="Load Route Data" id="load" onclick="load();">
<br>
<br>
<input type="text" id="search"><input type="button" value="Find Route" id="submit" onclick="clicked();"><br>
<br><br>
<div id="result">
</div>
</body>
</html>
There were a few mistakes within your code which prevented you from saving to localStorage. Here are some pointers.
Use localStorage.setItem() to save and localStorage.getItem() to retrieve data.
There's no need for creating a localStorage item for each bus route. LocalStorage can handle quite some data, depending on the browser and user browser settings. See What is the max size of localStorage values? for more info.
I'd simplify your data structure. Why put stops data into an array and then into an object? I've simplified this in my example.
When iterating over items use for (var i = 0; i < data.Routes.length; i++) { // your code here } another alternative is to user .map when iterating over items within an array.
Here's how to load data and save it to localStorage and into the app.
let BusSchdDataFromLocalStorage = [];
const load = () => {
// access localStorage and save the result in data
let data = JSON.parse(localStorage.getItem('routesInfo'));
if (data === null) {
// if no data is present, save InitialBusScheduleData to localStorage
localStorage.setItem('routesInfo', JSON.stringify(InitialBusScheduleData));
}
// Now that data is present in localStorage, read it.
data = JSON.parse(localStorage.getItem('routesInfo'));
if (data.Routes.length > 0) {
// if routes are present, save its data to a global var in our app
BusSchdDataFromLocalStorage = data;
statusEl.innerHTML = 'localStorage data present'
} else {
statusEl.innerHTML = 'localStorage data absent'
}
};
Here's how the search part works.
const search = () => {
const searchString = document.querySelector('#search').value;
// data from localStorage is present in the variable below
const routes = BusSchdDataFromLocalStorage.Routes;
// Filter route data based on the search input.
const busStopInfo = routes.reduce((stops, route) => {
return (route.routeName.toLowerCase() === searchString.toLowerCase()) ? route.stops : stops;
}, []);
const stops = Object.keys(busStopInfo);
// map over the stops and return the html structure with the stop number and the value
const results = stops
.map((stop) => '<div>' + stop + ' - ' + busStopInfo[stop] + '</div>')
.join('');
// add the html result to the result div.
resultEl.innerHTML = results.length > 0 ? results : 'No route found with that name.';
};
If you'd like to see the code in action. Here's a JSFiddle.