How to search in JSON file? - javascript

How could I create a search form that will search for the entered term inside a JSON file?
So if the search term is equal to a title inside the location object, it should return that object info. If there is no match, give the user feedback that there are no items found
I created a search input:
<form action="" method="get">
<input id="search" type="text" placeholder="Search term">
<input type="submit" class="button">
</form>
My JSON looks like this:
{
"title": "Locations",
"locations": [
{
"title": "New York",
"url": "api/newyork.js"
},{
"title": "Dubai",
"url": "api/dubai.js"
},{
"title": "Netherlands",
"url": "api/netherlands.js"
},{
"title": "Lanzarote",
"url": "api/lanzarote.js"
},{
"title": "Italy",
"url": "api/italy.js"
}
]
}
Update:
I came up with the following, using jQuery:
$("#search").change(function() {
var arrival = $(this).val();
$.getJSON( "api/videoData.js")
.done(function(data) {
// update your ui here
console.dir(data.pages);
var dataArr = data.pages;
// Iterate over each element in the array
for (var i = 0; i < dataArr.length; i++){
// look for the entry with a matching `code` value
if (dataArr[i].title == arrival){
// we found it
console.log(dataArr[i].title);
} else {
console.log('Houston, we have a problem');
}
}
}).fail(function(data) {
// handle error here
console.log('no results found');
});
});
This is working, only it should be entered exactly as stored in the JSON.
So when you search for "Italy" and you use lowercase type, it will not find the object. Also it will not give any entries when you search for example: ne. I would like to get Netherlands and New York as search results.

Here is a snippet that demonstrates how the lookup could be done. It only loads the JSON once. I have included the test data for when the JSON request fails, which it will in this demo. At every change of the input value a short list of matches is shown in a div below the input. The submit button is not included, as the search is immediate.
Try it.
// testData is defined for demo only. Can be removed
var testData = [
{
"title": "New York",
"url": "api/newyork.js"
},{
"title": "Dubai",
"url": "api/dubai.js"
},{
"title": "Netherlands",
"url": "api/netherlands.js"
},{
"title": "Lanzarote",
"url": "api/lanzarote.js"
},{
"title": "Italy",
"url": "api/italy.js"
}
];
// Variable to hold the locations
var dataArr = {};
// Load the locations once, on page-load.
$(function() {
$.getJSON( "api/videoData.js").done(function(data) {
window.dataArr = data.pages;
}).fail(function(data) {
console.log('no results found');
window.dataArr = testData; // remove this line in non-demo mode
});
});
// Respond to any input change, and show first few matches
$("#search").on('keypress keyup change input', function() {
var arrival = $(this).val().toLowerCase();
$('#matches').text(!arrival.length ? '' :
dataArr.filter(function(place) {
// look for the entry with a matching `code` value
return (place.title.toLowerCase().indexOf(arrival) !== -1);
}).map(function(place) {
// get titles of matches
return place.title;
}).join('\n')); // create one text with a line per matched title
});
// submit button is not needed really
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search" type="text" placeholder="Search term">
<div id="matches" style="height:70px; overflow-y:hidden; white-space:pre"></div>

The solution has a few steps, let us know which step you can't do:
Call a JS function when the user clicks the button
Read the file from disk on the server into a JS variable in the browser
Use lodash.js to get the url from the JS variable for the search term
Display results to the user
But taking a stab at the simplest answer, the way I'd do it:
Load your JSON file with the page, like any Javascript file.
Load lodash.js.
Call your JSON file towns.js and make it look like this:
var cities =
{
"title": "Locations",
"locations":
[
{
"title": "New York",
"url": "api/newyork.js"
},
{
"title": "Dubai",
"url": "api/dubai.js"
}
]
}
And then your HTML is something like this:
<input type="" class="button" onclick='go()'>
<script src='towns.js' />
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js' />
<script>
function go()
{
var name = document.getElementById('search').value;
var result = _.find(cities.locations, {'title': name});
if (!result)
window.alert('Nothing found');
else
window.alert('Go to ' + result.url);
}
<script>

If you intend your json file as static and not very huge one then you better load it at once on page load event(preventing sending the same requests dozens of times). Try this approach:
var xhr = new XMLHttpRequest(),
locations = {};
xhr.overrideMimeType("application/json");
xhr.open('GET', 'locations.json', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == "200") {
var content = JSON.parse(this.response),
locations_arr, i, count, title;
locations_arr = content['locations'];
for (i = 0, count = locations_arr.length; i < count; i++) {
title = locations_arr[i]['title'].toLowerCase();
locations[title] = locations_arr[i];
}
console.log(locations);
}
};
xhr.send(null);
Add some identifier to your form element (id="myForm") and 'name' attribute to your input field (name="search");
document.getElementById('myForm').onsubmit = function() {
var search_value = this.search.value.trim().toLowercase();
if (search_value && location[search_value]) {
console.log(location[search_value]);
} else {
alert("Object with specified title not found!");
}
// You must return false to prevent the default form behavior
return false;
}

As you said in your update with this code:
So when you search for "Italy" and you use lowercase type, it will not find the object. Also it will not give any entries when you search for example: ne. I would like to get Netherlands and New York as search results.
$("#search").change(function() {
var arrival = $(this).val();
$.getJSON( "api/videoData.js", { arrivalDate: arrival })
.done(function(data) {
// update your ui here
console.dir(data.pages);
var dataArr = data.pages;
// Iterate over each element in the array
for (var i = 0; i < dataArr.length; i++){
// look for the entry with a matching `code` value
if (dataArr[i].title == arrival){
// we found it
console.log(dataArr[i].title);
} else {
console.log('Houston, we have a problem');
}
}
}).fail(function(data) {
// handle error here
console.log('no results found');
});
});
Replace this line:
if (dataArr[i].title == arrival){
for
if (dataArr[i].title.toLowerCase().contains(arrival.toLowerCase())){
And now it will match all the string that contains the string that you are searching...

Related

How to add multiple parameters to the URL at once

Currently, if you add ?dc=bananas or ?dc=apples to the end of the URL, it shows that content, but how can I make it so that I can do up to 10? For example?dc=bananas+apples+oranges and it would show all content for the ID's added?
Also, is it possible to set it up so that I don't have a separate div for each one? Can this be done dynamically with one div checking for parameters in the URL?
HTML:
<div id="default-content" class="dynamic-content">
This is the default content
</div>
<!-- Dynamic Section 1 -->
<div id="apples" class="dynamic-content">
I like apples
</div>
<!-- Dynamic Section 2 -->
<div id="oranges" class="dynamic-content">
I like oranges
</div>
<!-- Dynamic Section 3 -->
<div id="bananas" class="dynamic-content">
I like bananas
</div>
CSS:
.dynamic-content {
display:none;
}
JS:
(function($){
// Parse the URL parameter
function getParameterByName(name, url) {
if (!url) {
url = location.href.split("?dc=").slice(-1)[0];
}
return url;
}
// Give the parameter a variable name
var dynamicContent = getParameterByName('dc');
$(document).ready(function() {
// Check if the URL parameter is apples
if (dynamicContent == 'apples') {
$('#apples').show();
}
// Check if the URL parameter is oranges
else if (dynamicContent == 'oranges') {
$('#oranges').show();
}
// Check if the URL parameter is bananas
else if (dynamicContent == 'bananas') {
$('#bananas').show();
}
// Check if the URL parmeter is empty or not defined, display default content
else {
$('#default-content').show();
}
});
})(jQuery);
Data:
const myProducts = [{
"productCode": "ABC",
"productNumber": "1467-6281",
"Name": "Example Item",
"Price": "1.975",
"Quantity": "4"
},
{
"productCode": "HJK",
"productNumber": "1111-9809",
"Name": "Example Item 2",
"Price": "2.975",
"Quantity": "14"
},
{
"productCode": "AOP",
"productNumber": "8792-3890",
"Name": "Example Item 3",
"Price": "3.975",
"Quantity": "8"
}]
You can have multiple parameters with the same name:
...?dc=bananas&dc=apples&dc=oranges
Which also allows you to easily create a div for each one:
// Use the URLSearchParams interface to get an array of
// all values for the parameter you want to query.
const urlParams = new URLSearchParams(window.location.search);
let dynamicContent = urlParams.getAll('dc');
// For each value in the array, create and insert
// a new element.
for (let dc of dynamicContent)
{
let newSection = document.createElement('div');
newSection.id = dc;
newSection.classList.add('dynamic-content');
newSection.innerText = 'I like ' + dc;
document.body.appendChild(newSection);
}
// Show or hide the default depending on if values exist.
if (dynamicContent.length) {
document.querySelector('.default-content').style.display = 'none';
}
else {
document.querySelector('.default-content').style.display = 'block';
}
The URLSearchParams interface is widely supported outside of Internet Explorer.
(function($) {
// Parse the URL parameter
function getParameterByName(name, url) {
return url ?? location.href.split(`?${name}=`)[1];
}
$(document).ready(function() {
const keys = getParameterByName('dc')?.split('+');
let dynamicContent;
// For each entry, check whether it correspondents to a div
if (keys)
keys.forEach(key => {
if (key == 'apples')
dynamicContent = $('#apples');
else if (key == 'oranges')
dynamicContent = $('#oranges');
else if (key == 'bananas')
dynamicContent = $('#bananas');
if (dynamicContent)
dynamicContent.show();
});
// If during the entire loop no element has been found, show the default one
if (!dynamicContent)
$('#default-content').show();
})
})(jQuery);
Side point: query-string library is great to convert a JS object to an url query string and get the raw query string to convert it to a JS object.
https://www.npmjs.com/package/query-string

How to access an attribute of a dynamically created element?

I am trying to get data stored in attributes of dynamically created elements that have been added to the DOM.
When a button is clicked, an AJAX call is made which returns a collection of objects. Using Javascript/JQuery, I am then adding new HTML elements for each of these returned objects, and adding their property values to attributes on the element. I am then adding a click listener to each of these dynamically created elements. When clicked I want to get the value in these attributes.
At the moment the id value is present, but the value of name is undefined.
For example, given the following HTML:
<input type="button" id="button" value="Get objects" />
<div id="objects-wrapper"></div>
Json response from server:
[
{
"id": 16,
"name": "eeeee",
},
{
"id": 17,
"name": "MIT Top New"
}
]
$(document).ready(function() {
$('#button').click(function() {
var id = $(this).attr('id');
var posting = $.post('/get/objects', {
id: id
});
posting.done(function(data) {
var objectsWrapper = $('#objects-wrapper');
objectsWrapper.empty();
if (data.length == 0) {
$(objectsWrapper).html('No objects found...');
} else {
$(objectsWrapper).empty();
for (i = 0; i < data.length; i++) {
var object = $('<div class="object">' + data[i].name + '</div>').on('click', function() {
var objectId = $(this).attr('id'); // <-- is present
var name = $(this).attr('name'); // <-- undefined
// do something with name
});
objectsWrapper.append(object);
}
}
});
return false;
});
});
The name value is present and is being used as the anchor text. Which I have verified as the following markup:
MIT Top New
How do I correctly get at the attribute name of the dynamically created anchor element?
You can use .find() of jQuery to find for the elements with selector in the DOM and make it work
Here is an example with your data.
var data = [
{
"id": 16,
"name": "eeeee",
},
{
"id": 17,
"name": "MIT Top New"
}
];
data.forEach(e=>{
$("#objects-wrapper").append(` </br> ${e.name} </br>`)
});
$("#button").click(function(){
$("#objects-wrapper").find("a").each(function(){
console.log($(this).attr("name"))
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="button" id="button" value="Get objects" />
<div id="objects-wrapper"></div>
By the way in your code, why you need to get it from DOM ? you're assigning the name yourself why not use it ? data[i].name this is the name right. Use it instead of getting from the DOM again

retrieving JSON object using a html link and displaying the json data on a html page

I want to retrieve a JSON object when the user clicks the html link. i want to retrieve it by it's ID and display everything in the object on the html page. I am also using session storage..... I want the user to be taken to the info.html page after they follow the html link.
my html code so far:
<div class="col">
<!-- team-img -->
<div class="team-block">
<div class="team-content">
<h4 class="text-white mb0">Chocolate Cake </h4>
<p class="team-meta">Large</p>
</div>
<div class="overlay">
<div class="text">
<h4 class="mb0 text-white"> Chocolate Cake </h4>
<p class="mb30 team-meta"> Large </p>
<p>Large Chocolate cake. 15 servings.</p>
<p>Further info</p>
</div>
</div>
</div>
</div>
The ID of the cake is "1" In the JSON file. I want all the details of the cake to be displayed when the user clicks the link. I want the user to be taken to info.html. The data of the cake which is stored in the JSON should be displayed there. The problem is with this line:
<p>Further info</p>
My JSON file is called cakes.json
below is my .js file
var ajax=function(url,success)
{
var ajaxRequest = new XMLHttpRequest();
var handleResponse=function()
{
if(ajaxRequest.readyState===4)
{
if(ajaxRequest.status===200)
{
var data=JSON.parse(ajaxRequest.responseText);
success(data); //this will call populateList
}
}
}
ajaxRequest.addEventListener("readystatechange",handleResponse,false);
ajaxRequest.open('GET', url, true);
ajaxRequest.send(null);
}
var navList;
var contentDiv;
function createHandler(car)
{
return function(){
sessionStorage.setItem("cake",JSON.stringify(cake));
}
}
function populateList(cakes)
{
navList=document.getElementById("nav");
contentDiv=document.getElementById("content");
cakes.forEach(function(cake){
var newLi=document.createElement("ul");
var newLink=document.createElement("a");
newLink.innerHTML=cake.name;
newLink.setAttribute("href","info.html");
newLink.addEventListener("click", createHandler(cake), false)
newLi.appendChild(newLink);
navList.appendChild(newLi);
})
}
function init(){
ajax("data/cakes.json",populateList);
}
init();
JSON file:
[
{
"id":1,
"cake":"chocolate cake",
"servings":"15",
"size":"10",
"code":"ed39"
},
]
Any Help would be greatly appreciated.
I've done an example of how this can be done using your structure of array containing multiple objects. Also I've modified your html added data-id parameter instead of id usually you don't save the id of an object in the id of html element(not best practice), you can use the data- params, there is also a function in jQuery that reads these parametes $(selector).data('identifier')
var json = [{
"id": 1,
"cake": "chocolate cake",
"servings": "15",
"size": "10",
"code": "ed39"
}, {
"id": 2,
"cake": "vanilla cake",
"servings": "15",
"size": "10",
"code": "ed39"
}];
$(document).on('click', '.cakeDetailsButton', function() {
var id = $(this).data('id'),
details = getDetailsById(id), //Here all the details for the cake
resultDiv = $('.result');
resultDiv.text('');
$.each(details, function(key, value) {
resultDiv.append(key + ' -- ' + value + '<br/>') // Here you can do whatever with the details
})
});
function getDetailsById(id) {
for (var i = 0; i < json.length; i++) {
if (json[i].id == id) {
return json[i];
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p><a class="cakeDetailsButton" href="#" data-id="1">Further info 1</a></p>
<p><a class="cakeDetailsButton" href="#" data-id="2">Further info 2</a></p>
<div class="result">
</div>
EDIT - Update
In order to add your json in the localStorage in order to access it from anywhere update the following
.js file
function populateList(cakes)
{
localStorage.setItem('cakesJson', cakes); //This adds an object to the localStorage with the key cakesJson and value the cakes object
navList=document.getElementById("nav");
contentDiv=document.getElementById("content");
cakes.forEach(function(cake){
var newLi=document.createElement("ul");
var newLink=document.createElement("a");
newLink.innerHTML=cake.name;
newLink.setAttribute("href","info.html");
newLink.addEventListener("click", createHandler(cake), false)
newLi.appendChild(newLink);
navList.appendChild(newLi);
})
}
Also then in the code provided by me above you should change the following
function getDetailsById(id) {
var json = localStorage.getItem("cakesJson"); // Here you retrieve the values stored in the localStorage
for (var i = 0; i < json.length; i++) {
if (json[i].id == id) {
return json[i];
}
}
}
The event listener remains the same as in the snippet
$(document).on('click', '.cakeDetailsButton', function() {
var id = $(this).data('id'),
details = getDetailsById(id), //Here all the details for the cake
resultDiv = $('.result');
resultDiv.text('');
$.each(details, function(key, value) {
resultDiv.append(key + ' -- ' + value + '<br/>') // Here you can do whatever with the details
})
});

Call field from another table in select firebase

I have a table categories and a documents. I want to create a new document and associate this document with a category already created.
The category table has the title field.
In the document creation form I want to put the categories already created in a select.
I tried doing this in the code below, but I ended up getting the select a key from the category -L0_xe_FIK9QdfTGhBDG for example, but I wanted the category title.
var documentosRef = firebase.database().ref('documentos');
var categoriasRef = firebase.database().ref('categorias');
var keyDocumento = ""
function initFirebase(){
categoriasRef.on('value', function(data) {
$('#categoria').html('');
for(categoria in data.val()){
option = "<option>"+categoria+"</option>"
$('#categoria').html($('#categoria').html()+option);
}
})
}
Structure:
{ "categorias": {
"L0_xe_FIK9QdfTGhBDG": {
"titulo": "Categorias 1"
},
"-L0a0FPFkXkKb3VNFN0c":{
"titulo": "Categorias 2"
}
}
Let check it out: JavaScript for/in Statement
var categorias = data.val();
for(var key in categorias){
option = "<option>"+categorias[key].titulo +"</option>"
$('#categoria').html($('#categoria').html()+option);
}

Pulling parts of JSON out to display them in a list

I have the following JSON:
var questions = {
section: {
"1": question: {
"1": {
"id" : "1a",
"title": "This is question1a"
},
"2": {
"id" : "1b",
"title": "This is question2a"
}
},
"2": question: {
"1": {
"id" : "2a",
"title": "This is question1a"
},
"2": {
"id" : "2b",
"title": "This is question2a"
}
}
}
};
NOTE: JSON changed based on the answers below to support the question better as the original JSON was badly formatted and how it works with the for loop below.
The full JSON will have 8 sections and each section will contain 15 questions.
The idea is that the JS code will read what section to pull out and then one by one pull out the questions from the list. On first load it will pull out the first question and then when the user clicks on of the buttons either option A or B it will then load in the next question until all questions have been pulled and then do a callback.
When the button in the appended list item is clicked it will then add it to the list below called responses with the answer the user gave as a span tag.
This is what I have so far:
function loadQuestion( $section ) {
$.getJSON('questions.json', function (data) {
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (item === $section) {
$('#questions').append('<li id="' + item.section.questions.question.id + '">' + item.section.questions.question.title + ' <button class="btn" data-response="a">A</button><button class="btn" data-response="b">B</button></li>');
}
}
});
}
function addResponse( $id, $title, $response ) {
$('#responses').append('<li id="'+$id+'">'+$title+' <span>'+$response+'</span></li>');
}
$(document).ready(function() {
// should load the first question from the passed section
loadQuestion( $('.section').data('section') );
// add the response to the list and then load in the next question
$('button.btn').live('click', function() {
$id = $(this).parents('li').attr('id');
$title = $(this).parents('li').html();
$response = $(this).data('response');
addResponse( $id, $title, $response );
loadQuestion ( $('.section').data('section') );
});
});
and the HTML for the page (each page is separate HTML page):
<div class="section" data-section="1">
<ul id="questions"></ul>
<ul id="responses"></ul>
</div>
I've become stuck and confused by how to get only the first question from a section and then load in each question consecutively for that section until all have been called and then do a callback to show the section has been completed.
Thanks
Do not have multiple id's in html called "section."
Do not have multiple keys in your JSON on the same level called "section". Keys in JSON on the same level should be unique just as if you are thinking about a key-value hash system. Then you'll actually be able to find the keys. Duplicate JSON keys on the same level is not valid.
One solution can be section1, section2, etc. instead of just section. Don't rely on data-section attribute in your HTML - it's still not good if you have "section" as the duplicate html id's and as duplicate JSON keys.
If you have only one section id in HTML DOM, then in your JSON you must also have just one thing called "section" e.g.:
var whatever = {
"section" : {
"1": {
"question" : {
"1" : {
"id" : "1a",
"title" : "question1a"
},
"2" : {
"id" : "2a",
"title" : "question2a"
}
}
},
"2": {
"question" : {
"1" : {
"id" : "1a",
"title" : "aquestion1a"
},
"2" : {
"id" : "2a",
"title" : "aquestion2a"
}
}
}
}
}
console.log(whatever.section[1].question[1].title); //"question1a"
To get question, do something like this:
function loadQuestions(mySectionNum) {
$.getJSON('whatever.json', function(data){
var layeriwant = data.section[mySectionNum].question;
$.each(layeriwant, function(question, qMeta) {
var desired = '<div id="question-' +
qMeta.id +
'"' +
'>' +
'</div>';
$("#section").append(desired);
var quest = $("#question-" + qMeta.id);
quest.append('<div class="title">' + qMeta.title + '</div>');
//and so on for question content, answer choices, etc.
});
});
}
then something like this to actually get the questions:
function newQuestion(){
var myHTMLSecNum = $("#section").attr('data-section');
loadQuestions(myHTMLSecNum);
}
newQuestion();
//below is an example, to remove and then append new question:
$('#whatevernextbutton').on('click',function(){
var tmp = parseInt($("#section").attr('data-section'));
tmp++;
$("#section").attr('data-section', tmp);
$("#section").find('*').remove();
newQuestion();
});
Technically your getJSON function always retrieves the same data. Your code never compares the id given to the id you're extracting.
Your getJSON should look something like:
function loadQuestion( $section ) {
for (var i = 0; i < questions.section.length; i++) {
var item = questions.section[i];
if (item.id === $section) {
for (var j = 0; j < item.questions.length; j++) {
$('#questions').append('<li id="' +
item.questions[i].id + '">' +
item.questions[i].title +
' <button class="btn" data-response="a">A</button><button class="btn" data-response="b">B</button></li>'
);
}
}
}
}
Modify your JSON to:
var questions = {
section: [{
id: 1,
questions: [{
id: "1a",
title: "This is question1a"
},{
id: "2a",
title: "This is question2a"
}]},{
id: 2,
questions: [{
id: "1a",
title: "This is question1a"
},{
id: "2a"
title: "This is question2a"
}]
}]
};
Edit: your first parameter of getJSON is the URL of the JSON returning service.
You don't need getJSON at all if your JSON is already defined on the client. I have modified the code above.

Categories