Replace jQuery with vanilla JS when parsing JSON, and other improvements - javascript

I would like get rid of jQuery for this simple task, but I don't know how to use vanilla JS to do the job. The page works fine, but I recognize that it is a mess, and I would like to improve the invisible, under the hood code.
I am a complete noob, I will try to explain what I've done but I need your help to simplify as much as possible and clean the code (one function only / alternative to javascript in "href" / everything else that comes to mind). And to get rid of a 88kB .js file just to call a function.
The web page contains two links that point to local Icecast streams.
When one selects a stream by clicking a link,
the common audio player loads the corresponing source URL (function changeStream());
the link is highligted and every other link returns to the default CSS (function changeBg());
the variable number is set to the correspondibg element number to allow title parsing;
the jQuery function getTitle() is executed.
Additional notes:
HTML IDs with "*-js" means that they are modified by a javascript function;
I may have mispelled something below, I have rewritten variables and other parts with easier to understand names;
Getting rid of jQuery is my priority, but other improvements are well accepted.
Thank you in advance
The webpage contains is
<!doctype html>
<html lang="en">
<head>[...]</head>
<body>
<div class="links">
<a id="/sourceA-js" class="sourceLink"
href="javascript:changeStream('/radio/sourceA'); changeBg('/sourceA-js'); var number=0; getTitle();">
<span class="sourceText">Stream A</span></a>
<a id="/sourceB-js" class="sourceLink"
href="javascript:changeStream('/radio/sourceB'); changeBg('/sourceB-js'); var number=1; getTitle();">
<span class="sourceText">Stream B</span></a>
</div>
<div id="currentTrackInfo">
<p>Track: <span id="currentTrackTitle-js">Please select a radio stream</span>
</p>
<audio id="radio-js" class="radioPlayer" controls>
<source src="" type="audio/mpeg">
</audio>
</div>
<script>
function changeBg (streamId) {
var boxes = document.getElementsByClassName('sourceLink'),
i = boxes.length;
while (i--) {
boxes[i].removeAttribute('style');
}
document.getElementById(streamid).setAttribute('style', 'color:grey;background-color:red;');
}
function changeStream (stream) {
document.getElementById('radio-js').pause();
document.getElementById('radio-js').setAttribute('src', stream);
document.getElementById('radio-js').load();
document.getElementById('radio-js').play();
}
</script>
<script src="/static/js/jquery.js"></script>
<script>
function getTitle () {
jQuery.get('status-json.xsl', {}, function (response) {
$('#currentTrackTitle-js').html(response.icestats.source[number]['title']);
document.title = response.icestats.source[number]['title'];
});
}
gettitle();
setInterval(gettitle, 15000);
</script>
</body>
</html>
The parsed file, status-json.xsl, contains
{
"icestats": {
"admin": "mail",
"host": "domain",
"location": "name",
"server_id": "version",
"server_start": "humandate",
"server_start_iso8601": "computerdate",
"source": [
{
"audio_info": "bitrate=320",
"bitrate": 320,
"genre": "Jazz",
"listener_peak": 2,
"listeners": 1,
"listenurl": "address",
"server_description": "streamdescription",
"server_name": "streamname",
"server_type": "audio/mpeg",
"server_url": "/radio/jazz",
"stream_start": "humandate",
"stream_start_iso8601": "computerdate",
"title": "author - title",
"dummy": null
},
{
"audio_info": "bitrate=320",
"bitrate": 320,
"genre": "Jazz",
"listener_peak": 2,
"listeners": 1,
"listenurl": "address",
"server_description": "streamdescription",
"server_name": "streamname",
"server_type": "audio/mpeg",
"server_url": "/radio/jazz",
"stream_start": "humandate",
"stream_start_iso8601": "computerdate",
"title": "author - title",
"dummy": null
}
]
}
}

This appears to be the only jQuery code that you want to replace:
jQuery.get("status-json.xsl", {}, function(response) {
$('#currentTrackTitle-js').html(response.icestats.source[number]['title']);
document.title = response.icestats.source[number]['title'];
});
Vanilla JavaScript equivalent to jQuery.get() is fetch. This will read JSON string from file and convert it to a JavaScript object:
fetch("status-json.xsl")
.then(response => {
// parse JSON from 'status-json.xsl' file
return response.json();
})
.then(status => {
// perform your app logic here
});
This jQuery code:
$('#currentTrackTitle-js').html(response.icestats.source[number]['title']);
can be replaced with this vanilla JavaScript:
const currTitle = document.getElementById('currentTrackTitle-js');
currTitle.innerHTML = status[number]['title'];
Putting it all together:
const currTitle = document.getElementById('currentTrackTitle-js');
fetch("status-json.xsl")
.then(response => {
return response.json();
})
.then(status => {
currTitle.innerHTML = status.icestats.source[number]['title'];
document.title = status.icestats.source[number]['title'];
});

Related

How to pass json file name to javascript function on button click?

I have a Javascript file that returns some HTML Content based on the content of a json file. In other words, in my file called "Resources" I have multiple json files and a single HTML File that contains multiple buttons. It can be more clear with an example, here is the HTML file:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div class="cards">
<div class="card">
<img src="first.jpg" class="card_image">
<a href="javascript:showMenu()" class="animated-button"> <!--showMenu() is inside menu-card.js-->
<span></span>
<span></span>
<span></span>
<span></span>
<p>FIRST BUTTON</p>
</a>
</div>
<div class="card">
<img src="second.jpg" class="card_image">
<a href="javascript:showMenu()" class="animated-button"> <!--showMenu() is inside menu-card.js-->
<span></span>
<span></span>
<span></span>
<span></span>
<p>SECOND BUTTON</p>
</a>
</div>
</div>
<script type="text/javascript" src="first.json"></script>
<script type="text/javascript" src="second.json"></script>
<script type="text/javascript" src="menu_card.js"></script>
</body>
</html>
Here is first.json :
products =
`[
{
"name": "Shoes",
"description": "This are some shoes.",
"price": "180"
}
]`;
Here is second.json :
products =
`[
{
"name": "Hoodies",
"description": "This are some hoodies.",
"price": "90"
}
]`;
Finally, here is menu_card.js :
var storedMenu = JSON.parse(products);
//Some other stuff using storedMenu
Anyways, since I have multiple json files for different "categories" for my project and a SINGLE javascript file "menu_card.js" that must output some HTML content based on the json content, the problem is that I already have a lot of json files and they all have the same object name and I don't plan on changing it for every single file ( products = ... ). Is it possible to maybe pass the json file name from the href inside the HTML to the javascript function that will use it for the JSON.parse()?
I think I was clear enough with my issue, if something is unclear or not specified, I can easily edit the post. Thank you in advance!
Well you can change the script import like that
<script type="text/javascript" src="first.json">
fetch('/first.json')
.then(response => response.json())
.then( json => {
window.products = (window.products || []);
json.forEach( p => window.products.push(p) );
});
</script>
But files must be pure JSON
[
{
"name": "Hoodies",
"description": "This are some hoodies.",
"price": "90"
}
]
Rewrite them programmaticaly.
You could also rewrite then like below but it will change all the solution.
{
"name": "Hoodies",
"description": "This are some hoodies.",
"price": "90"
}
And so on... so everything will be available in window.products.
As it can be long to rewrite, you can do it for every file with something like
<script type="text/javascript" src="first.json">
Promise.all([ '/first.json', '/second.json']
.map( file => fetch(file).then( r => r.json() ) )
)
.then( allProducts => allProducts.flat() )
.then( allProducts => window.products = allProducts)
;
});
</script>

handlebars and JSON data coming from a fetch

I have the code below and have 2 separate issues, so please bear with me on this:
Issue 1 [fetch ?]:
The data displayed doesn't change when the JSON change. Sounds like it's a cache issue as I can't see any HTTP request beside the original one. How can I force the JSON file to be downloaded again each time?
Issue 2 [handlebars ?]: with $(document.body).append(html); in the loop, it keeps re-writing the instead of editing the values. How can I change this?
Here is the code:
javascript.js:
async function fetch_json() {
try {
var resp = await fetch('http://localhost:8000/data.json', {mode: 'cors'});
var jsonObj = await jsonify(resp);
return jsonObj;
} catch (error) {
// all errors will be captured here for anything in the try block
console.log('Request failed', error);
}
}
html page:
<script id="handlebars-demo" type="text/x-handlebars-template">
<div>
{{#each this}}
Name : {{name}} Value : {{value}} <br>
{{/each}}
</div>
</script>
<script type="text/javascript">
var test_data = [{ "name" : "john doe", "value" : "developer" },{ "name" : "bob boby", "value" : "developer2" }];
setInterval(function() {
test_data = fetch_json()
.then(function(result) {
html = templateScript(result);
//$(document.body).append(html);
})
}, 1000);
var template = document.getElementById('handlebars-demo').innerHTML;
Compile the template data into a function
var templateScript = Handlebars.compile(template);
var html = templateScript(test_data);
$(document.body).append(html);
</script>
any help would be the most appreciated, thank you!
You should create a DOM element to hold the HTML you are generating. I've created <div id="content"></div> in the example.
You can use $().html() to overwrite the HTML each time instead of appending.
$('#content') selects the DOM element with id=content and then overwrite the HTML inside .html(string) with string.
A common approch to cache busting is to attach a timestamp to the url as a url query param, which I have done by concatenating nocache='+new Date().getTime().
In normal use in production a unique identifier is usually generated per version for each resource after building.
// for demo purposes, overwrite value property with username property
jsonify = x => x.json().then(x => x.map(x => ({ ...x,
value: x.username
})));
async function fetch_json() {
try {
// append timestamp to prevent caching
var resp = await fetch('https://jsonplaceholder.typicode.com/users?nocache=' + new Date().getTime(), {
mode: 'cors'
});
var jsonObj = await jsonify(resp);
return jsonObj;
} catch (error) {
// all errors will be captured here for anything in the try block
console.log('Request failed', error);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.6/handlebars.js" integrity="sha256-ZafrO8ZXERYO794Tx1hPaAcdcXNZUNmXufXOSe0Hxj8=" crossorigin="anonymous"></script>
<div id="content"></div>
<script id="handlebars-demo" type="text/x-handlebars-template">
<div>
{{#each this}} Name : {{name}} Value : {{value}} <br> {{/each}}
</div>
</script>
<script type="text/javascript">
var test_data = [{
"name": "john doe",
"value": "developer"
}, {
"name": "bob boby",
"value": "developer2"
}];
setInterval(function() {
test_data = fetch_json()
.then(function(result) {
html = templateScript(result);
$('#content').html(html);
})
}, 2000);
var template = document.getElementById('handlebars-demo').innerHTML;
//Compile the template data into a function
var templateScript = Handlebars.compile(template);
var html = templateScript(test_data);
$('#content').html(html);
</script>

Navigating a json file to create desired array for jquery autocomplete

I am programming an autocomplete function for a search bar that features names of places in Norway.
I collect the data from a REST api URL provided by a third party.
Example with input "st" and two results:
{
"sokStatus":{
"ok":"true",
"melding":""
},
"totaltAntallTreff":"81280",
"stedsnavn":[
{
"ssrId":"23149",
"navnetype":"By",
"kommunenavn":"Larvik",
"fylkesnavn":"Vestfold",
"stedsnavn":"Stavern",
"aust":"214841.84",
"nord":"6550500.29",
"skrivemaatestatus":"Godkjent",
"spraak":"NO",
"skrivemaatenavn":"Stavern",
"epsgKode":"25833"
},
{
"ssrId":"506202",
"navnetype":"By",
"kommunenavn":"Stord",
"fylkesnavn":"Hordaland",
"stedsnavn":"Stord",
"aust":"-32194.93",
"nord":"6665261.05",
"skrivemaatestatus":"Godkjent",
"spraak":"NO",
"skrivemaatenavn":"Stord",
"epsgKode":"25833"
}
]
}
I want to have the autocomplete array contain the "stedsnavn" features from all the returned results in the json file. so for the above example it would be [Stavern, Stord].
I built my code based off a template/tutorial i found online. When I run it now the autocomplete suggestion is the "totaltAntallTreff" feature so for the json above it would suggest 81280.
Edit: What I really need to know is how to properly query the json where I now only have response(data). I have tried several methods ($.map, $.each) but whenever I modify my code it ends up giving no autocomplete suggestions.
See my code below
$(function () {
var getData = function (request, response) {
$.getJSON(
"https://ws.geonorge.no/SKWS3Index/ssr/json/sok?antPerSide=5&eksakteForst=false&navn=" + request.term + "*",
function (data) {
(response(data));
});
};
var selectItem = function (event, ui) {
$("#myText").val(ui.item.value);
return false;
}
$("#myText").autocomplete({
source: getData,
select: selectItem,
minLength: 1,
change: function() {
$("#myText").val("").css("display", 2);
}
});
});
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css" />
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery.ui.autocomplete.css" />
<script src="http://code.jquery.com/jquery-1.8.3.js"></script>
<script src="http://code.jquery.com/ui/1.10.0/jquery-ui.js"></script>
<div id="menu-container">
<input type="text" id="myText" />
</div>
Given the JSON structure provided, you could get the result with the following:
let json_data = {
"sokStatus": {
"ok": "true",
"melding": ""
},
"totaltAntallTreff": "81280",
"stedsnavn": [{
"ssrId": "23149",
"navnetype": "By",
"kommunenavn": "Larvik",
"fylkesnavn": "Vestfold",
"stedsnavn": "Stavern",
"aust": "214841.84",
"nord": "6550500.29",
"skrivemaatestatus": "Godkjent",
"spraak": "NO",
"skrivemaatenavn": "Stavern",
"epsgKode": "25833"
},
{
"ssrId": "506202",
"navnetype": "By",
"kommunenavn": "Stord",
"fylkesnavn": "Hordaland",
"stedsnavn": "Stord",
"aust": "-32194.93",
"nord": "6665261.05",
"skrivemaatestatus": "Godkjent",
"spraak": "NO",
"skrivemaatenavn": "Stord",
"epsgKode": "25833"
}
]
}
let values = json_data.stedsnavn.map(item => item.skrivemaatenavn);
values.forEach(value => {
$("#list").append(`<li>${value}</li>`);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="list"><ul>
As to what json_data.stedsnavn.map(item => item.skrivemaatenavn); is doing:
json_data.stedsnavn.map(item => item.skrivemaatenavn);
// Get the "stedsnavn" key from the data, an array
// Map each object in the array to
// its "skrivemaatenavn" key

Hogen.js - Load nested template with JS

I got a postings template (Hogen.js) were I load data (api call from Laravel 5.3) into it.
I managed to load, compile and render the template and the data correctly.
Problem:
I have jquery/vanilla js scripts that need to work with the template and the data but somehow this JS is completely ignored by the rendered template and it doesn't work (onClick, other ajax calls etc.).
My load/render JS:
var $page = 1;
var source = $("#postTemplate").html();
var template = Hogan.compile(source);
$.ajax({
url: '/api/postings',
data: { page: $page} ,
type: 'POST',
success: function(data) {
var output = template.render(data);
$('.posts-container').prepend(output);
}
});
My Template:
<script id="postTemplate" type="text/x-hogan-template">
#{{#posts.data}}
<div class="post">
<div class="image">
<img src="#{{ imageURL }}" alt="post image" />
</div>
<div class="info">
<div class="like-count" data-like-id="#{{ id }}">
more html
</div>
</div>
#include('partials.comments')
</div>
#{{/posts.data}}
</script>
I include a partial from laravel with my "comment" code that needs to be execuded aswell (fadeIn, ajaxacalls,submit etc.)
Is it possible, that I cann ot execute my JS with the newly rendered template or DOM, because it's not available at document.ready?
Do I need to switch my template engine? Any other way to make this work?
JSON:
{
"success": true,
"posts": {
"total": 46,
"per_page": 20,
"current_page": 3,
"last_page": 3,
"next_page_url": null,
"prev_page_url": "http://localhost/api/postings?page=2",
"from": 41,
"to": 46,
"data": {
"40": {
"id": 6,
"name": " ",
"imageURL": "",
"city": "Spanien",
"country": "",
"created_at": "2018-03-11 09:40:25",
"profilePictureURL": null,
"social_src": 0,
"mediumImageURL": null
}
}
}
}
I stripped it down a bit!
You cannot use
#include('partials.comments')
in your hgan.js template. Hogan is (almost) logicless. It is for binding JSON to HTML templates, it is not capable or intended for this use.
Partials can only be used like folows:
var partialText = "normal text but can use {{foo}} is from a variable";
var p = Hogan.compile(partialText);
var text = "This template contains a partial ({{>partial}})."
var t = Hogan.compile(text);
var s = t.render({foo: chickens}, {partial: p});
is(s, "This template contains a partial (normal text but we can use chickens. is a variable).", "partials work");
Basically {{>partial}} can be used to nest another precompiled template.

can't find error in my code?

I am making a simple search code. I can't find error. The error message says Uncaught SyntaxError: Unexpected identifier on javascript line 40 (target=document.getElementById("outputPlace").
Do not look at the button, I have not added event listener to it yet.
I just want that when I press enter products are displayed.
HTML CODE
<html>
<head>
<title>Price List </title>
</head>
<body>
<h1> PRICELIST </h1>
<form id="formSearch">
<div>
<label for="searchBox"> Search products here: </label>
<input type="text" placeholder="Type text here to search product" id="searchBox">
</div>
<div id="buttons">
<button id="getAll"> GET ALL PRODUCTS</button>
</div>
</form>
<div id="outputPlace">
</div>
<script src="product.js"></script>
</body>
</html>
JAVASCRIPT CODE
(function(){ //start anonymous function
var list= {
"listOfProducts": [
{
"name":"hard disk",
"price": "50$",
"quality":"good",
},
{
"name":"monitor",
"price":"100$",
"quality": "very good",
},
{
"name":"speakers",
"price":"20$",
"quality": "not bad",
},
{
"name":"headphones",
"price":"12$",
"quality":"bad",
},
{
"name": "mobile phone",
"price": "300$",
"quality": "excellent",
},
{
"name": "usb memory",
"price": "30$",
"quality": "the best",
}
]
},
var target=document.getElementById("outputPlace"),
searchForm=document.getElementById("formSearch"),
productList=list.listOfProducts,
listLength=productList.length,
searchValue=document.getElementById("searchBox"),
searchInput=searchValue.value;
var listMethods = {
searchList: function(event) {
event.preventDefault();
var i;
target.innerHTML="";
if(listLength>0 && searchInput!=="") {
for(i=0;i<listLength;i++) {
var product=productList[i],
whatIsFound=product.name.indexOf(searchInput);
if(whatIsFound!==-1){
target.innerHTML+='<p>'+product.name+', '+product.price+', '+product.quality+'click here to buy</p>'
}
}
}
},
};
searchForm.addEventListener("submit",listMethods.searchList,false);
}) (); //end anonymous function
You have a comma after that large JSON object you defined at the top of your JavaScript, followed by another var.
var list= {
"listOfProducts": [
{
"name":"hard disk",
"price": "50$",
"quality":"good",
},
...[a bunch of stuff]...
},
var target=document.getElementById("outputPlace"),
searchForm=document.getElementById("formSearch"),
productList=list.listOfProducts,
listLength=productList.length,
searchValue=document.getElementById("searchBox"),
searchInput=searchValue.value;
Both of the two other proposed answers would fix this (well ok Otome deleted their answer which was to drop the second var).
Change this
var list = {
...
},
var target=document.getElementById("outputPlace"),
to this:
var list = {
...
};
var target=document.getElementById("outputPlace"),
And you have one more comma at the end of script, after }

Categories