If statement for when an array has no value (Javascript + HTML) - javascript

I have an input box storing various texts into an array and it is displaying on another page, but I want the page to say "No Events" when there is no value in the array
here is the javascript that appends the array to the page
function getEvents(){
//get the localstorage from the new event page
events = JSON.parse(localStorage.getItem("events"));
for (var i = 0; i < events.length; i++)
{
if (events === "undefined" || null || "") {
document.getElementById("none").innerHTML = "No events";
} else {
// title output
var title = document.createElement("h1"); //creates h1 element
var titleText = document.createTextNode(events[i].name); //assigns title
text
title.appendChild(titleText); //appends text to h1
document.getElementById("output").appendChild(title); //appends to the
page
// date output
var date = document.createElement("p"); //creates p element
var dateText = document.createTextNode(events[i].date); //assigns date
text
date.appendChild(dateText); //appends text to p
document.getElementById("output").appendChild(date); //appends to the page
}
}
}
And here is the html on the event loader page
<body onload="getEvents()">
<!-- Title -->
<center>
<h1>My Events</h1>
</center>
<p id="none"></p>
<div id="output">
</div>
</body>

You are looping through the array so you should test on the array elements not the array itself. Your for loop won't even run if there is no array. So the if that runs tests on the array itself under the scope of the loop has basically no sense.
You can proceed this way just before going beneath the loop:
if (events.length == 0)
document.getElementById("none").innerHTML = "No events";

Take your if statement out of the for loop
Use proper syntax for the OR clauses
properly test for undefined
Also test for length == 0
if (toString.call(events) == "undefined" || events.length == 0) {
document.getElementById("none").innerHTML = "No events";
} else {
for(){/*...*/}
}

Related

Can't copy in the first click

I can't copy value from textarea in the first click.
im working with Popup Box.
i think the problem is im hidding the .footer-result-box for textarea.
im using code like this :
HTML :
<div id="popup-box">
<div class="footer-result-box" style="height:0px">
<textarea id="final-interest"></textarea> //textarea
</div>
<button class="btn-copy-all"> COPY </button> //copy button
</div>
jQuery :
$(".btn-copy-all").click(function() {
//im use this code for get value from table when row selected.
if (states.activeScreen == "interest") {
var datas = new_tabel_interest.rows(".selected").data();
}
if (states.activeScreen == "related") {
var datas = new_tabel_page_interest.rows(".selected").data();
}
res = "";
for (i = 0; i < datas.length; i++) {
if (res != "") res += ",";
res += datas[i][2];
}
//EXECUTE
$("#final-interest").val(res); //texarea get value from selected row
$(".footer-result-box").animate({
height: "205px"
}); //display box
$("#final-interest").select(); //select textarea value
document.execCommand('copy'); //copy the result
$("#popup-box").css({
display: "none"
}); //close popup-box
alert("Copied!");
});
The code running well, but the result not copied.
My Goal :
Success copy textarea value then close the #popup-box.
Textarea Value will get DATA from Table when im select the row table.

Get second, third and so on values

I have this problem here
The problem has been solved, but my question is how can I get the second value from that, or the third one. The sheet will have many tables and at some point I will need a total for each table. Also, is there any solution to automatically find the the array number which contain date row for each table (instead defining this manually). Hope my explanation make sense.
Thank you!
Kind regards,
L.E. Test file
If I understood your question correctly, instead of breaking the loop when a match to "Total" is found do whatever is needed to be done within the loop like so...
var today = toDateFormat(new Date());
var todaysColumn =
values[5].map(toDateFormat).map(Number).indexOf(+today);
var emailDate = Utilities.formatDate(new Date(today),"GMT+1",
"dd/MM/yyyy");
for (var i=0; i<values.length; i++){
if (values[i][0]=='Total'){
nr = i;
Logger.log(nr);
var output = values[nr][todaysColumn];
// Do something with the output here I"m assuming you email it
}
}
The loop will keep going and find every "Total" and do the same thing. This answer assumes that the "Totals" are in the same column. You can get fancier with this if you only want certain tables to send and not others, but this should get you started.
I didn't quite understand the second part of your question...
"Also, is there any solution to automatically find the the array
number which contain date row for each table (instead defining this
manually). Hope my explanation make sense."
I'm guessing you want all the rows that contain "Total" in the specific column. You could instantiate a variable as an empty array like so, var totals = [];. Then instead of sending the email or whatever in the first loop you would push the row values to the array like so, totals.push(nr+1) . //adding 1 gives you the actual row number (rows count from 1 but arrays count from 0). You could then simply loop through the totals array and do whatever you wanted to do. Alternatively you could create an array of all the values instead of row numbers like totals.push(values[nr][todaysColumn]) and loop through that array. Lots of ways to solve this problem!
Ok based on our conversation below I've edited the "test" sheet and updated the code. Below are my edits
All edits have been made in your test sheet and verified working in Logger. Let me know if you have any questions.
Spreadsheet:
Added "Validation" Tab
Edited "Table" tab so the row with "Email Address" in Column A lines up with the desired lookup values (dates or categories)...this was only for the first two tables as all the others already had this criteria.
Code:
Create table/category selector...
In the editor go to File >> New >> HTMLfile
Name the file "inputHTML"
Copy and paste the following code into that file
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form class="notice_form" autocomplete="off" onsubmit="formSubmit(this)" target="hidden_iframe">
<select id="tables" onchange="hideunhideCatagory(this.value)" required></select>
<p></p>
<select id="categories" style="display:none"></select>
<hr/>
<button class="submit" type="submit">Get Total</button>
</form>
<script>
window.addEventListener('load', function() {
console.log('Page is loaded');
});
</script>
<script
src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
// The code in this function runs when the page is loaded.
$(function() {
var tableRunner = google.script.run.withSuccessHandler(buildTableList);
var catagoryRunner = google.script.run.withSuccessHandler(buildCatagoryList);
tableRunner.getTables();
catagoryRunner.getCategories();
});
function buildTableList(tables) {
var list = $('#tables');
list.empty();
list.append('<option></option>');
for (var i = 0; i < tables.length; i++) {
if(tables[i]==''){break;}
list.append('<option>' + tables[i] + '</option>');
}
}
function buildCatagoryList(categories) {
var list = $('#categories');
list.empty();
list.append('<option></option>');
for (var i = 0; i < categories.length; i++) {
if(categories[i]==''){break;}
list.append('<option>' + categories[i] + '</option>');
}
}
function hideunhideCatagory(tableValue){
var catElem = document.getElementById("categories");
if(tableValue == "Total Calls By Date" || tableValue == "Total Appointments by Date"){
catElem.style.display = "none"
document.required = false;
}else{
catElem.style.display = "block"
document.required = true;
}
}
function formSubmit(argTheFormElement) {
var table = $("select[id=tables]").val(),
catagory = $("select[id=categories]").val();
console.log(table)
google.script.run
.withSuccessHandler(google.script.host.close)
.getTotal(table,catagory);
}
</script>
</body>
<div id="hiframe" style="display:block; visibility:hidden; float:right">
<iframe name="hidden_iframe" height="0px" width="0px" ></iframe>
</div>
</html>
Edits to Code.gs file
Replace code in Code.gs with this...
//This is a simple trigger that creates the menu item in your sheet
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Run Scripts Manually')
.addItem('Get Total','fncOpenMyDialog')
.addToUi();
}
//This function launches the dialog and is launched by the menu item
function fncOpenMyDialog() {
//Open a dialog
var htmlDlg = HtmlService.createHtmlOutputFromFile('inputHTML')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(200)
.setHeight(150);
SpreadsheetApp.getUi()
.showModalDialog(htmlDlg, 'Select table to get total for');
};
//main function called by clicking "Get Total" on the dialogue...variables are passed to this function from the formSubmit in the inputHTML javascript
function getTotal(table,catagory) {
function toDateFormat(date) {
try {return date.setHours(0,0,0,0);}
catch(e) {return;}
}
//get all values
var values = SpreadsheetApp
.openById("10pB0jDPG8HYolECQ3eg1lrOFjXQ6JRFwQ-llvdE2yuM")
.getSheetByName("Tables")
.getDataRange()
.getValues();
//declare/instantiate your variables
var tableHeaderRow, totalRow, tableFound = false;
//begin loop through column A in Tables Sheet
for (var i = 0; i<values.length; i++){
//test to see if values have already been found if so break the loop
if(tableFound == true){break;}
//check to see if value matches selected table
if (values[i][0]==table){
//start another loop immediately after the match row
for(var x=i+1; x<values.length; x++){
if(values[x][0] == "Email Address"){ //This header needs to consistantly denote the row that contains the headers
tableHeaderRow = x;
tableFound = true;
}else if(values[x][0] == "Total"){
totalRow = x;
break;
}
}
}
}
Logger.log("Header Row = "+tableHeaderRow)
Logger.log("Total Row = "+ totalRow)
var today = toDateFormat(new Date())
var columnToTotal;
if(catagory==''){
columnToTotal = values[tableHeaderRow].map(toDateFormat).map(Number).indexOf(+today);
}else{
columnToTotal = values[tableHeaderRow].indexOf(catagory);
}
var output = values[totalRow][columnToTotal];
Logger.log(output);
var emailDate = Utilities.formatDate(new Date(today),"GMT+1", "dd/MM/yyyy");
//here is where you would put your code to do something with the output
}
/** The functions below are used by the form to populate the selects **/
function getTables(){
var cFile = SpreadsheetApp.getActive();
var cSheet = cFile.getSheetByName('Validation');
var cSheetHeader = cSheet.getRange(1,1,cSheet.getLastRow(),cSheet.getLastColumn()).getValues().shift();
var tabelCol = (cSheetHeader.indexOf("Tables")+1);
var tables = cSheet.getRange(2,tabelCol,cSheet.getLastRow(),1).getValues();
return tables.filter(function (elem){
return elem != "";
});
}
function getCatagories(){
var cFile = SpreadsheetApp.getActive();
var cSheet = cFile.getSheetByName('Validation');
var cSheetHeader = cSheet.getRange(1,1,cSheet.getLastRow(),cSheet.getLastColumn()).getValues().shift();
var catagoriesCol = (cSheetHeader.indexOf("Catagory")+1);
var catagories = cSheet.getRange(2,catagoriesCol,cSheet.getLastRow(),1).getValues();
return catagories.filter(function (elem){
return elem != "";
});
}

Having trouble with onchange and storing numbers in an array via textbox

I am having trouble with my onchange not working and storing numbers in an array via a text box.
What I want the code to do is to get statistics on the numbers inputted into the text box. I do this by having the user input numbers into the text box and hit the Enter key to display those numbers. The numbers should be put into an array before being put into a list to display the inputted numbers. However, I keep getting this error where the onchange is not triggering when hitting the Enter key or clicking off of the text box.
Here is an image of the error I am getting when inspecting the code
With the numbers stored in the array, I want to try to get the Mean of the numbers. But, I keep getting the error "NaN" which makes me think that my numbers are not getting stored into the array properly.
Here is the code:
<html>
<head>
<title>Stats</title>
</head>
<p>Array is called numbers. numbers.sort();</p>
<div id="stats">
<input type ="text" id="value" onchange="list()"> <!-- Getting the Onchange Error here -->
<button id="button1" onclick = "list()">Enter</button>
<ul id="list1">
</ul>
<button id="stat_button" onclick="calculateMean()">Get Statistics</button>
<p id="mean">Mean= </p>
</div>
<script>
function list() {
var liElement = document.createElement("li"); //Creating new list element//
var ulElement = document.getElementById("list1"); //Get the ulElement//
var input = document.getElementById("value").value; //Get the text from the text box//
var numbers = []; //create Array called numbers
numbers.push(input);//adds new items to the array
//for loop//
for(var i=0; i < numbers.length; i++) {
liElement.innerHTML = numbers[0]; //Puts the array into the list for display//
ulElement.appendChild(liElement); //add new li element to ul element//
}
}
function calculateMean() {
var meanTotal = 0;
var meanAverage = 0;
var meanArray = [];
for (var i = 0; i < meanArray.length; i++) {
meanTotal += meanArray[i];
}
meanAverage = (meanTotal / meanArray.length);
document.getElementById("mean").innerHTML = meanAverage;
}
</script>
Try adding it through addEventListener instead of inline like that:
document.getElementById('value').addEventListener('change', function(e){
list()
})
The reason the Mean is always NaN is because your mean array is always an empty array when you start with. I think you were referring to a numbers array here.
You will have to declare the array outside the scope of the 2 functions, since it is the common to both.
And it is always a better idea to decouple Javascript and HTML. Bind your events in JS instead of inline event handlers.
Note: When you read the value from the input, it is a string, so convert it to a number before storing it in the numbers array.
HTML
<p>Array is called numbers. numbers.sort();</p>
<div id="stats">
<input type="text" id="value">
<!-- Getting the Onchange Error here -->
<button id="button1">Enter</button>
<ul id="list1">
</ul>
<button id="stat_button">Get Statistics</button>
<p id="mean">Mean= </p>
</div>
JS
document.getElementById('value').addEventListener('change', list);
document.getElementById('button1').addEventListener('click', list);
document.getElementById('stat_button').addEventListener('click', calculateMean);
var numbers = [];
function list() {
var liElement = document.createElement("li"); //Creating new list element//
var ulElement = document.getElementById("list1"); //Get the ulElement//
var input = document.getElementById("value").value; //Get the text from the text box//
numbers.push(input); //adds new items to the array
//for loop//
for (var i = 0; i < numbers.length; i++) {
liElement.innerHTML = numbers[0]; //Puts the array into the list for display//
ulElement.appendChild(liElement); //add new li element to ul element//
}
}
function calculateMean() {
var meanTotal = 0;
var meanAverage = 0;
for (var i = 0; i < numbers.length; i++) {
meanTotal += numbers[i];
}
meanAverage = (meanTotal / numbers.length);
document.getElementById("mean").innerHTML = meanAverage;
}
jsFiddle

How to get innerHTML of DIV without few inside DIV's?

I have some DIV, what contains HTML with images, styles e.t.c. I want to remove exact div's that contains id = 'quot' or className = 'quote', but i don't understand how i can get not only innerHTML of each tag. For example, < p > and < /p > which don't have innerHTML also should be included in final parsed HTML.
var bodytext = document.getElementById("div_text");
var NewText = "";
if (bodytext.hasChildNodes){
var children = bodytext.childNodes;
for (var i = 0; i < children.length; i++){
if (children[i].id != "quot" && children[i].className != "quote" && children[i].innerText != ""){
NewText = NewText + children[i].innerHTML;
}
}
HTML of source need to be parsed:
<div id="div_text">
<p>
Some Text</p>
<p>
Some Text</p>
<p>
<img alt="" src="localhost/i/1.png" /></p>
<div id="quot" class="quote" />
any text <div>text of inside div</div>
<table><tr><td>there can be table</td></tr></table>
</div>
<p>
</p>
</div>
Desired output:
<p>
Some Text</p>
<p>
Some Text</p>
<p>
<img alt="" src="localhost/i/1.png" /></p>
<p>
</p>
Just grab a reference to the targeted divs and remove them from their respective parents.
Perhaps something a little like this?
EDIT: Added code to perform operation on a clone, rather than the document itself.
div elements don't have .getElementById method, so we search for an element manually.
window.addEventListener('load', myInit, false);
function removeFromDocument()
{
// 1. take car of the element with id='quot'
var tgt = document.getElementById('quot');
var parentNode = tgt.parentNode;
parentNode.removeChild(tgt);
// 2. take care of elements whose class == 'quote'
var tgtList = document.getElementsByClassName('quote');
var i, n = tgtList.length;
for (i=0; i<n; i++)
{
// we really should be checking to ensure that there aren't nested instances of matching divs
// The following would present a problem - <div class='quote'>outer<div class='quote'>inner</div></div>
// since the first iteration of the loop would also remove the second element in the target list,
parentNode = tgtList[i].parentNode;
parentNode.removeChild(tgtList[i]);
}
// 3. remove the containing div
var container = document.getElementById('div_text');
container.outerHTML = container.innerHTML;
}
function cloneAndProcess()
{
var clonedCopy = document.getElementById('div_text').cloneNode(true);
var tgt;// = clonedCopy.getElementById('quot');
var i, n = clonedCopy.childNodes.length;
for (i=0; i<n; i++)
{
if (clonedCopy.childNodes[i].id == 'quot')
{
tgt = clonedCopy.childNodes[i];
var parentNode = tgt.parentNode;
parentNode.removeChild(tgt);
break; // done with for loop - can only have 1 element with any given id
}
}
// 2. take care of elements whose class == 'quote'
var tgtList = clonedCopy.getElementsByClassName('quote');
var i, n = tgtList.length;
for (i=0; i<n; i++)
{
// we really should be checking to ensure that there aren't nested instances of matching divs
// The following would present a problem - <div class='quote'>outer<div class='quote'>inner</div></div>
// since the first iteration of the loop would also remove the second element in the target list,
parentNode = tgtList[i].parentNode;
parentNode.removeChild(tgtList[i]);
}
// 3. remove the containing div
//var container = clonedCopy; //.getElementById('div_text');
//container.outerHTML = container.innerHTML;
console.log(clonedCopy.innerHTML);
}
function myInit()
{
cloneAndProcess();
//removeFromDocument();
}

A good JavaScript to add/remove items from/to array?

folks! Today I created this script that has the following functionality:
add new items to array
list all items from the array
remove an item from the array
There are two functions:
addToFood() - adds the value of input to the array and updates
innerHTML of div
removeRecord(i) - remove a record from the array and updates
innerHTML of div
The code includes 3 for loops and you can see it at - http://jsfiddle.net/menian/3b4qp/1/
My Master told me that those 3 for loops make the solution way to heavy. Is there a better way to do the same thing? Is it better to decrease the loops and try to use splice? Thanks in advance.
HTML
<!-- we add to our foodList from the value of the following input -->
<input type="text" value="food" id="addFood" />
<!-- we call addToFood(); through the following button -->
<input type="submit" value="Add more to food" onClick="addToFood();">
<!-- The list of food is displayed in the following div -->
<div id="foods"></div>
JavaScript
var foodList = [];
function addToFood () {
var addFood = document.getElementById('addFood').value;
foodList.push(addFood);
for (i = 0; i < foodList.length; i++) {
var newFood = "<a href='#' onClick='removeRecord(" + i + ");'>X</a> " + foodList[i] + " <br>";
};
document.getElementById('foods').innerHTML += newFood;
}
function removeRecord (i) {
// define variable j with equal to the number we got from removeRecord
var j = i;
// define and create a new temporary array
var tempList = [];
// empty newFood
// at the end of the function we "refill" it with the new content
var newFood = "";
for (var i = 0; i < foodList.length; i++) {
if(i != j) {
// we add all records except the one == to j to the new array
// the record eual to j is the one we've clicked on X to remove
tempList.push(foodList[i]);
}
};
// make redefine foodList by making it equal to the tempList array
// it should be smaller with one record
foodList = tempList;
// re-display the records from foodList the same way we did it in addToFood()
for (var i = 0; i < foodList.length; i++) {
newFood += "<a href='#' onClick='removeRecord(" + i + ");'>X</a> " + foodList[i] + " <br>";
};
document.getElementById('foods').innerHTML = newFood;
}
You should use array.splice(position,nbItems)
function removeRecord (i) {
foodList.splice(i, 1); // remove element at position i
var newFood = "";
for (var i = 0; i < foodList.length; i++) {
newFood += "<a href='#' onClick='removeRecord(" + i + ");'>X</a> "
+ foodList[i] + " <br>";
};
document.getElementById('foods').innerHTML = newFood;
}
http://jsfiddle.net/3b4qp/5/
Now using JQuery:
$(function(){
$(document).on('click','input[type=submit]',function(){
$('#foods')
.append('<div>X '
+ $('#addFood').val() + '</div>');
});
$(document).on('click','.item',function(){
$(this).parent().remove();
});
});
http://jsfiddle.net/jfWa3/
Your problem isn't the arrays, your problem is this code:
node.innerHTML += newFood;
This code is very, very, very slow. It will traverse all exising DOM nodes, create strings from them, join those strings into one long string, append a new string, parse the result to a new tree of DOM nodes.
I suggest to use a framework like jQuery which has methods to append HTML fragments to existing DOM nodes:
var parent = $('#foods');
...
for (var i = 0; i < foodList.length; i++) {
parent.append( "<a href='#' onClick='removeReco..." );
That will parse the HTML fragments only once.
If you really must do it manually, then collect all the HTML in a local string variable (as suggested by JohnJohnGa in his answer) and then assign innerHTML once.
Here's some tips to, at least, make your code more portable (dunno if it will be better performance wise, but should be, since DOM Manipulation is less expensive)
Tips
First separate your event handle from the HTML
Pass the "new food" as a function paramater
Tie the array elements to the DOM using the ID
Instead of rerendering everything when something changes (using innerHTML in the list), just change the relevant bit
Benefits:
You actually only loop once (when removing elements from the array).
You don't re-render the list everytime something changes, just the element clicked
Added bonus: It's more portable.
Should be faster
Example code:
FIDDLE
HTML
<div id="eventBinder">
<!-- we add to our foodList from the value of the following input -->
<input id="addFood" type="text" value="food" />
<!-- we call addToFood(); through the following button -->
<button id="addFoodBtn" value="Add more to food">Add Food</button>
<!-- The list of food is displayed in the following div
-->
<div id="foods"></div>
</div>
JS
// FoodList Class
var FoodList = function (selectorID) {
return {
foodArray: [],
listEl: document.getElementById(selectorID),
idCnt: 0,
add: function (newFood) {
var id = 'myfood-' + this.idCnt;
this.foodArray.push({
id: id,
food: newFood
});
var foodDom = document.createElement('div'),
foodText = document.createTextNode(newFood);
foodDom.setAttribute('id', id);
foodDom.setAttribute('class', 'aFood');
foodDom.appendChild(foodText);
this.listEl.appendChild(foodDom);
++this.idCnt;
},
remove: function (foodID) {
for (var f in this.foodArray) {
if (this.foodArray[f].id === foodID) {
delete this.foodArray[f];
var delFood = document.getElementById(foodID);
this.listEl.removeChild(delFood);
}
}
}
};
};
//Actual app
window.myFoodList = new FoodList('foods');
document.getElementById('eventBinder').addEventListener('click', function (e) {
if (e.target.id === 'addFoodBtn') {
var food = document.getElementById('addFood').value;
window.myFoodList.add(food);
} else if (e.target.className === 'aFood') {
window.myFoodList.remove(e.target.id);
}
}, false);
Here is another sugestion:
function remove(arr, index) {
if (index >= arr.lenght) { return undefined; }
if (index == 0) {
arr.shift();
return arr;
}
if (index == arr.length - 1) {
arr.pop();
return arr;
}
var newarray = arr.splice(0, index);
return newarray.concat(arr.splice(1,arr.length))
}

Categories

Resources