JavaScript
Appearance
Basics
Load after html loading via defer
<script src="main.js" defer></script>
HTML Form Elements
Function of button click
<button id="submit" onclick="myFunction()">Anmelden</button>
Function on enter
<input type="number" id="threshold" name="threshold" min="50" max="500" step="1" value="300">
var input_threshold = document.getElementById("threshold");
input_threshold.addEventListener("keydown", function (e) {
// Enter is pressed
if (e.keyCode === 13) {
event.preventDefault();
myFunction();
}
}, false);
Variables
Declaration
const var = 123; // constant: no change possible let var = 123; // block-bound, perferred to var var var = 123; // old style, better use let instead
Clone/Copy Object
// from https://www.samanthaming.com/tidbits/70-3-ways-to-clone-objects/ const myObject2 = Object.assign({}, myObject);
Arrays
Loop over all elements
var rows = table.getRows();
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
...
}
Loop over array of objects
for (let i = 0; i < Object.keys(array).length; i++) {
const key = Object.keys(array)[i];
const value = array[key];
}
NOTE: do not use
for (const key in array) { ... }
as this results in this problem
"The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype"
Element in Array
if(myArray.indexOf(myItem) > -1) {...}
Last element of array
my_data.slice(-1)[0]
or
my_data.slice(-1)[0]["myKey"]
Array of Objects
delete some object properties
// delete not needed object properties
const allowedKeys = new Set(["type", "name", "x_date", "x_url", ...Object.keys(measures)]);
data.forEach((obj) => {
Object.entries(obj).forEach(([key]) => {
if (!allowedKeys.has(key)) {
delete obj[key];
}
});
// extract year from x_date and add as property
obj.year = parseInt(obj.x_date.substr(0, 4));
});
Extract distinct list/set list for property 'type' values
const act_types = [...new Set(data.map((obj) => obj.type))].sort();
Sort by object property "measure" DESC
data = data.sort((a, b) => b[measure] - a[measure]);
Async fetching via JQuery
in HTML
<script src="lib/jquery-3.5.0.min.js"></script>
in JS
// array of promises for async fetching
const promises = [];
// ref dictionary to be fetched: Country Code -> Country Name
var mapCountryNames = {};
// fetch countries-latest-all.json containing country reference data like code and continent
function fetch_mapRefCountryData(mapCountryNames) {
const url =
"https://entorb.net/COVID-19-coronavirus/data/int/countries-latest-all.json";
return $.getJSON(url, function (data) {
console.log("success: mapCountryNames");
})
.done(function (data) {
console.log("done: mapCountryNames");
$.each(data, function (key, val) {
mapCountryNames[data[key].Code] = data[key].Country;
});
})
.fail(function () {
console.log("fail: mapCountryNames");
});
}
// Start the async fetching
promises.push(fetch_mapRefCountryData(mapCountryNames, mapContinentCountries));
// Wait for all async promises to be done (all data is fetched), then print message
Promise.all(promises).then(function () {
console.log("All data fetched");
});
Date handling
// calculate date via offset const daysOffset = 7; const s_data_last_date = "2020-04-01" const ts_last_date = Date.parse(s_data_last_date) var minDate = new Date(ts_last_date); minDate.setDate(minDate.getDate() + daysOffset);
Helper Functions
from [https://love2dev.com/blog/javascript-remove-from-array/
function arrayRemove(arr, value) {
return arr.filter(function (ele) { return ele != value; });
}
from [1]
function removeAllOptionsFromSelect(select) {
var i, L = select.options.length - 1;
for (i = L; i >= 0; i--) {
select.remove(i);
}
}
// Formats value "Something_Is_HERE" to "Something is here" like sentence
// value: The value to format
// separator: the separator string between words
function formatValueToSentenceLike(value, separator) {
const allLowerCaseValue = value.split(separator).join(" ").toLowerCase();
return allLowerCaseValue[0].toUpperCase() + allLowerCaseValue.substr(1);
}
// modifies array of objects by removing if value == keys
function arrayRemoveValueTextPairByValue(arr, key) {
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i].value == key) { arr.splice(i, 1); }
}
}
Local Storage
// read browser's local storage for last session data
localStorageData = window.localStorage.getItem("my_data");
if (localStorageData) {
var my_data = JSON.parse(localStorageData);
} else {
var my_data = [];
}
Export and Import / Download and Upload data to Variable
HTML
<a id="downloadAnchor" style="display:none"></a> <button id="download_data" onclick="download_data()">Export</button> and Import: <input id="upload_data" type="file" onchange="upload_data(this)">
JS
// download data
function download_data() {
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify([settings, data]));
let html_dl_anchor = document.getElementById("downloadAnchor");
html_dl_anchor.setAttribute("href", dataStr);
html_dl_anchor.setAttribute("download", "eta.json");
html_dl_anchor.click();
}
// upload data
function upload_data(input) {
// from https://javascript.info/file
let file = input.files[0];
let reader = new FileReader();
reader.readAsText(file);
reader.onload = function () {
const uploaded_data = JSON.parse(reader.result);
settings = uploaded_data[0];
data = uploaded_data[1];
};
reader.onerror = function () {
console.log(reader.error);
};
}
Hide/remove HTML elements
HTML
< div id="text_intro" >...< /div >
JS
function hide_intro() {
// from https://stackoverflow.com/questions/1070760/javascript-href-vs-onclick-for-callback-function-on-hyperlink
const html_text_intro = document.getElementById('text_intro');
html_text_intro.remove();
}
Internet Explorer Backward Compatibility
Error: Object doesn't support property or method 'includes'
variant 1:
replace:
if(myarray.includes(key)) {
by:
if(myarray.indexOf(key) > -1) {
variant 2: polyfile
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) { start = 0; }
return this.indexOf(search, start) !== -1;
};
}
Tabulator
// wait for tableBuilt event
table.on("tableBuilt", function () {
myUpdateTableFnc();
});
eCharts
Jest Unittests
install jest and jsdom for testing browser features like window.localStorage
npm install --save-dev jest jest-environment-jsdom
in package.json set
"scripts": {"test": "jest --coverage","testc": "jest --coverage"},
"jest": { "testEnvironment": "jsdom" }
run jest
npm test npm run testc
In the file I want to test here `helper.js`
var module = module || {};
module.exports = {
zeroPad,
...
}
The testcasefile `helper.test.js`
describe("Testing zeroPad", () => {
const { zeroPad } = require("./helper");
test("1->01", () => {
expect(zeroPad(1, 2)).toEqual("01");
});
});
test.each
To test many cases use
describe("zeroPad()", () => {
const { zeroPad } = require("./helper");
const cases = [
// arg1, arg2, expectedResult
[1, 2, "01"],
[6, 3, "006"],
];
test.each(cases)(
"given '%p', '%p' it shall return %p",
(arg1, arg2, expectedResult) => {
expect(zeroPad(arg1, arg2)).toEqual(expectedResult);
}
);
});
ignore Test Coverage for missing else etc
place such a comment before the non-relevant line
/* istanbul ignore else */ /* istanbul ignore next */
Translations / i18n
const supportedLanguages = ['de', 'en', 'fr']
const langBrowser = navigator.language.slice(0, 2)
const lang = supportedLanguages.includes(langBrowser) ? langBrowser : 'en'
const messages = await import(`./locales/${lang}.json`).catch(() => import('./locales/en.