Why does this Javascript function rewrite the innerHTML of one div but not the other? - mysql

I'm using NodeJS to query a MySQL database for a single entry of a journal, but the results aren't going to both of the assigned divs. I have an iFrame in my center column, dedicated to two divs (one hidden at any given time). One div is a read-only page for the journal entry, and the other one contains a TinyMCE rich-text editor. I have buttons in left column to switch between the views.
The rich-text editor loads properly on initial load of page, but doesn't update as I navigate with the calendar; the read-only innerHTML does update properly as I navigate.
calDt[] is an array that holds dates. calDt[0] is the active date, while calDt[1] holds a dummy date used for navigating the calendar without changing the entry.
app.js:
app.get('/getdata/:dateday', (req, res) => {
let sql = `SELECT entry FROM main where dateID ='${req.params.dateday}'`
let query = db.query(sql, (err, results) => {
if(err) {
throw err
}
res.send(JSON.stringify(results));
})
})
middle-left.ejs
<button style= "height:22px"; type="button" onclick="readDivHere()">Lock</button>
<button style= "height:22px"; type="button" onclick="editDivHere()">Edit</button></div>
<script> // the Lock button brings us back to the completed entry in the middle stuff
function readDivHere() {
document.getElementById('frame1').contentWindow.readDivHere();
document.getElementById('frame1').scrolling = "yes";
}
</script>
<script> // the Edit button will bring tinymce rich-text editor to the middle stuff
function editDivHere() {
document.getElementById('frame1').contentWindow.editDivHere();
document.getElementById('frame1').scrolling = "no";
}
</script>
middle-center.ejs
<iframe id="frame1" class="entryon" src="" frameborder="0px"></iframe>
<script>
document.getElementById("frame1").src = "iframe";
</script>
iframe.ejs
<div id="readDiv" class="here" style="display: block; background: white; padding-top: 0px; padding-left: 10px; padding-right: 8px; min-height: 810px; width: 967px;"><%- include ('entry'); %></div>
<div id="editDiv" class="here" style="display: none; padding: 0px;" ><%- include ('editPage'); %></div>
<script> //function that switches from rich-text editor back to real entry
function readDivHere() { // here we run a function to update text of read-only entry
document.getElementById("readDiv").style.display="block";
document.getElementById("editDiv").style.display="none";
}
</script>
<script> //function that switches from read-only entry to rich-text editor
function editDivHere() {
document.getElementById("readDiv").style.display="none";
document.getElementById("editDiv").style.display="block";
}
</script>
entry.ejs
<div id="readOnlyEntry"></div>
<script>
// load the active entry into the middle column for proper reading
function loadEntry(p) {
var x = parent.calDt[1].getFullYear();
var y = parent.calDt[1].getMonth();
y = y + 1;
if (y < 10) {
y = "0" + y;
};
if (p < 10) {
p = "0" + p;
}
var newDate = x + "" + y + "" + p; // p is a date formatted like 20210808
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                const text = this.responseText;
                const obj = JSON.parse(text);
document.getElementById("readOnlyEntry").innerHTML = obj[0].entry;
document.getElementById("richTextEd").innerHTML = obj[0].entry; // doesn't work!
            }
xhttp.open("GET", "../getdata/" + newDate, true);
        xhttp.send();
}
</script>
<script>
// rich-text editor populates correctly on load
loadEntry(parent.calDt[0].getDate());
</script>
editPage.ejs
<%- include ('tinymce'); %>
<form method="POST" action="../result" enctype="multipart/form-data">
<textarea name="content" id="richTextEd">Here's the default text.</textarea>
</form>
calendar-clicker.ejs
var p = x.innerHTML; // get the value of the calendar cell text (i.e. day of the month)
p = p.match(/\>(.*)\</)[1];
var d = calDt[1].getFullYear(); // what year is the calendar referencing?
var q = calDt[1].getMonth(); // what month is the calendar referencing?
q = q + 1; // compensate for javascript's weird month offset
calDt[0] = new Date(q + "/" + p + "/" + d); // assign a new global date variable
calDt[1] = calDt[0]; // temporarily reset navigation date to active date
document.getElementById('frame1').contentWindow.loadEntry(p);
Does the failure have to do with assigning the innerHTML to a different .ejs? If I put the form into the same div as the read-only entry, the form still fails to update as I navigate.

Solved it.
In entry.ejs, I replaced...
document.getElementById("richTextEd").innerHTML = obj[0].entry;
with
tinymce.get("richTextEd").setContent(obj[0].entry);
https://www.tiny.cloud/blog/how-to-get-content-and-set-content-in-tinymce/

Related

Countdown using astrisk

Currently it shows smallest to biggest. I want it start from biggest to smallest, can't figure it out.
Current output of code
Sample run:
Input to text box: 3
Output in div:
[*]
[**]
[***]
And that’s all folks!
Desired output of code
Sample Run:
Input to text box: 3
Output in div:
[***]
[**
[*]
And that’s all folks!
<!doctype html>
<!—This code will produce a triangle of stars -->
<!-- ================================== -->
<html>
<head>
<title> Countdown </title>
<script type="text/javascript">
function Countdown() {
var count, astrisk;
i=1;
//j=0;
count = parseFloat(document.getElementById('countBox').value);
document.getElementById('outputDiv').innerHTML = '';
astrisk = '*'
while (i<count) {
j=0;
while (j<i) {
document.getElementById('outputDiv').innerHTML =
document.getElementById('outputDiv').innerHTML + astrisk ;
j=j+1;
}
document.getElementById('outputDiv').innerHTML =
document.getElementById('outputDiv').innerHTML + '<br>';
i=i+1;
}
document.getElementById('outputDiv').innerHTML =
document.getElementById('outputDiv').innerHTML + 'And that\'s all folks!' + '<br>';
}
</script>
</head>
<body>
<p>
Start of the countdown:
<input type="text" id="countBox" size=4 value=10>
</p>
<input type="button" value="Begin Countdown" onclick="Countdown();">
<hr>
<div id="outputDiv"></div>
</body>
</html>
I tried flipping some variables, couldn't exactly get it to do what I want. Most of my attempts have led to my computer crashing, I feel like answer is there, but I can't exactly see it. code isn't properly formatted, spent 2 hours trying to indent correctly, and eventually I gave up.
Get the input, button and p (output)
Add click event to the button
When the button is clicked, get input.value plus 1 since it's automatically decreased at the beginning of the function
Print to the DOM the asterisk repeated by the value (that is decreased every 1 second).
When the value reaches 0, clear the function.
let btn = document.querySelector('button');
let input = document.querySelector('input');
let output = document.querySelector('p');
// Button onclick
btn.addEventListener('click', () => {
// Save the run time (input value + 1) since it's automatically decreased at run-time
let timesRun = Number(input.value) + 1;
// Disable the button when it's counting
btn.disabled = true;
// Repeated function every 1 second
let interval = setInterval(function(){
// Decrease the run time
timesRun -= 1;
// If reaches 0, clear the function
if(timesRun === 0){
btn.disabled = false;
clearInterval(interval);
output.innerHTML = 'TIMEOUT';
// Else? print the * repeated by times remain
} else {
output.innerHTML = '*'.repeat(timesRun);
}
}, 1000);
});
<input type="number" placeholder="Countdown" />
<button>Start</button>
<div>
<p></p>
</div>
Updated with the following conditions:
Check that the input is a valid number and not empty
Accept a number between 1 and 10 (including)
let btn = document.querySelector('button');
let input = document.querySelector('input');
let output = document.querySelector('p');
// Button onclick
btn.addEventListener('click', () => {
// Check it's a valid nubmer and not empty
if(input.value.length === 0 || isNaN(Number(input.value))) {
output.innerHTML = 'Please type a valid number.';
// Check if the number is between 1 and 10
} else if (Number(input.value) <= 0 || Number(input.value) > 10) {
output.innerHTML = 'Please type a number between 1-10';
} else {
// Save the run time (input value + 1) since it's automatically decreased at run-time
let timesRun = Number(input.value) + 1;
// Disable the button when it's counting
btn.disabled = true;
// Repeated function every 1 second
let interval = setInterval(function(){
// Decrease the run time
timesRun -= 1;
// If reaches 0, clear the function
if(timesRun === 0){
btn.disabled = false;
clearInterval(interval);
output.innerHTML = 'TIMEOUT';
// Else? print the * repeated by times remain
} else {
output.innerHTML = '*'.repeat(timesRun);
}
}, 1000);
}
});
<input type="number" placeholder="Countdown" />
<button>Start</button>
<div>
<p></p>
</div>

Is it possible to track Active Cell change in Google Sheet Sidebar

I'm writing a sidebar which displays additional information based on selected row/cell by user. It's rendering fine on sidebar open, but if the user changes active cell, I need to update content.
Apparently, I can add a "refresh" button in sidebar, but I really want to avoid clicking "refresh" every time. Putting it on timer also isn't very good cause will just spam with unnecessary requests to sheet app.
Has anyone ever did something similar and that approach did you use?
Maybe it's possible somehow to get event about user changing active cell into the sidebar javascript code?
I've put together a prototype of a sidebar that collects all the cell the user clicks in. Starting with an onSelectionChange() trigger to record the cells the user clicks in and recording them to PropertyService Document Properties, when the user moves the mouse over the sidebar the cells that were selected will show up.
First we have a simple Sidedbar
HTML_Sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<textarea id="textArea" rows="10" cols="35" onmouseenter="onMouseEnter()">
</textarea>
<?!= include('JS_Sidebar'); ?>
</body>
</html>
Next we have the client side code
JS_Sidebar.html
<script>
function onMouseEnter() {
try {
google.script.run.withSuccessHandler(
function(selections) {
let textArea = document.getElementById("textArea");
let text = textArea.value.trim();
selections.forEach( cell => {
text = text + "You clicked cell "+cell+"\n";
}
);
textArea.value = text;
}
).getLatestSelections();
}
catch(err) {
alert(err);
}
}
</script>
Now for all the server side code.
Code.gs
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function showSidebar() {
var html = HtmlService.createTemplateFromFile("HTML_Sidebar");
html = html.evaluate();
SpreadsheetApp.getUi().showSidebar(html);
}
function onSelectionChange(event) {
try {
// event = {"range":{"columnEnd":3,"columnStart":3,"rowEnd":1,"rowStart":1},"authMode":"LIMITED","source":{},"user":
// {"email":"xxxxxxxxxx#gmail.com","nickname":"xxxxxxxxx"}}
let properties = PropertiesService.getDocumentProperties();
let property = properties.getProperty("_Selections");
if( !property ) property = "";
property = property + JSON.stringify(event) + "\n";
properties.setProperty("_Selections",property);
}
catch(err) {
Logger.log("Error in onSelectionChange: "+err)
}
}
function getLatestSelections() {
try {
let properties = PropertiesService.getDocumentProperties();
let property = properties.getProperty("_Selections");
if( !property ) property = "";
properties.deleteProperty("_Selections");
properties = property.split("\n");
properties.pop(); // remove the last \n element
return properties.map( property => {
let cell = JSON.parse(property);
return getRangeA1(cell.range.rowStart,cell.range.columnStart);
}
);
}
catch(err) {
Logger.log("Error in getLatestSelections: "+err)
}
}
function getRangeA1(row,col) {
try {
let colNum = col;
let colName = "";
let modulo = 0;
while( colNum > 0 ) {
modulo = (colNum - 1) % 26;
colName = String.fromCharCode(65 + modulo) + colName;
colNum = Math.floor((colNum - modulo) / 26);
}
colName = colName+(row);
return colName;
}
catch(err) {
throw "Error in getRangeA1: "+err;
}
}
Screen shot
I understand that you want to record when the active cells change on a Sheet, and you want to do it without resorting to timers or buttons. If my understanding of your scenario is correct, then your best bet is using onSelectionChange triggers.
You can introduce an onSelectionChange trigger in your script to execute a refresh every time that the user changes the active cell. Leave a comment below if you have questions about this approach.

how to change the div value with a counter

I don't understand why this function does not change the value of num every time I press the button. basically i wanted to make a counter that adds one to the div every time i press the button.
<div class="elements" id="txtthrow">0</div>
<button class="button1" id="buttonNewHand" onclick="newFaceTot()">new<br>number</button>
//------------------------------------------------------------------------
refthrow = document.getElementById("txtthrow");
function newFaceTot(){
numberChanged();
}
function numberChanged(){
var num = parseInt(refthrow.value, 10);
refthrow.innerHTML = changeNumber(num);
}
function changeNumber(num){
num = num + 1;
return num;
}
Only form (e.g. input/select/textarea) elements have a value. For other elements use innerHTML or innerText to get the content.
const refthrow = document.getElementById("txtthrow");
function newFaceTot() {
numberChanged();
}
function numberChanged() {
var num = parseInt(refthrow.innerText, 10);
refthrow.innerHTML = changeNumber(num);
}
function changeNumber(num) {
num = num + 1;
return num;
}
<div class="elements" id="txtthrow">0</div>
<button class="button1" id="buttonNewHand" onclick="newFaceTot()">new<br>number</button>
Therefore the value attribute was undefined. And parseInt(undefined) is NaN (not a number).

how to create html table results from JSON data

I have code that uses AJAX and JSON to output a chunk of SQL data when you do a search and I am trying to separate the data some and have it display into an HTML table. At first it was just the SQL data but I put some tags into the innerHTML line to at least visually separate it, however I would really like to be able to put each column into a separate table cell. Any ideas on how to do that would be greatly appreciated. Here is the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/jquery-2.2.2.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<link href="css/bootstrap.min.css" rel="stylesheet">
<title>AJAX Search Example</title>
<script>
function fetch() {
// (A) GET SEARCH TERM
var data = new FormData();
data.append('search', document.getElementById("search").value);
data.append('ajax', 1);
// (B) AJAX SEARCH REQUEST
var xhr = new XMLHttpRequest();
// (CHANGE1) USING ONREADYSTATECHNAGE INSTEAD OF ONLOAD
xhr.onreadystatechange = function (event) {
// (CHANGE2) we will check if ajax process has completed or not it goes from 1,2,3,4 means end.
if(this.readyState == 4){
// (CHANGE2) when ready state comes to 4 we then check what response status was it if it is 200 good else error.
if(this.status == 200){
// (CHANGE3) MOVED ALL YOUR CODE HERE
// (CHANGE4) we need to use responseText instead of response because JSON comes as string that is why we are parsing it to be converted into array
var results = JSON.parse(this.responseText);
//I have added just a measure to check what the out put is you can remove it latter. open dev console to get the result.
console.log(results);
wrapper = document.getElementById("results");
if (results.length > 0) {
wrapper.innerHTML = "";
// (CHANGE5) UPDATED data ref with results
for (i = 0; i < results.length; i++) {
let line = document.createElement("div");
//it is just as simple to create id only it must start with alphabet not number
line.id=`res${[i]}`;
//we created span tag to display price and this is what we will change. on that span we will create a data-price attribute which will hold original price and we will run calculations using that number
//BIG CHANGE
//BIG CHANGE
//since after parsing individual record will be in Js object so we dont need to access them like array results[i]['item']
//we access them with dot notation results[i].item
line.innerHTML = `Category:${results[i].category} - OEM #:${results[i].oemnumber} - Price:$<span data-price='${results[i].price}'>${results[i].price}</span>
select discount >>
%70
%60
%50 100%`;
wrapper.appendChild(line);
}
// (CHANGE6) We moved event listeners here so any newly added elements will be updated.
//get all the links and apply event listener through loop
var links = document.querySelectorAll('a');
for ( ii = 0; ii < links.length; ii++) {
links[ii].addEventListener("click", function(event) {
//capture link value and get number to be converted to percentage
var percentage = event.target.innerText.match(/\d+/)[0]/100;
//capture the data-price which is within same div as anchor link
var pricetarget = event.target.parentElement.querySelector('[data-price]');
//get value of data-price
var actualprice= pricetarget.dataset.price;
//run math and chnage the value on display
pricetarget.innerHTML=(actualprice*percentage).toFixed(2);
});
}
} else { wrapper.innerHTML = "No results found"; }
} else {
//if reponse code is other ethan 200
alert('INTERNET DEAD OR AJAX FAILED ');
}
}
};
// (CHANGE7) We moved open event to end so everything is ready before it fires.
xhr.open('POST', "2-search.php");
xhr.send(data);
return false;
};
</script>
</head>
<body>
<!-- (A) SEARCH FORM -->
<form ID='myForm' onsubmit="return fetch();">
<h1>SEARCH FOR CATALYTIC CONVERTER</h1>
<input type="text" id="search" required/>
<input type="submit" value="Search"/>
</form>
<!-- (B) SEARCH RESULTS -->
<div id="results"></div>
</body>
</html>
Here is where I added the tags to at least visually separate it: "line.innerHTML = `Category:${results[i].category} - OEM #:${results[i].oemnumber} - Price:$${results[i].price}"
What I want to do is have Category, OEM #, and price each in a separate table cell. Thank you for any help offered.
You can simply generate trs inside your for (i = 0; i < results.len.. like you are already doing for divs . So , just use += to append every new tr inside tbody and then append this to your table
Demo Code :
//suppose json look like below :)
var results = [{
"category": "A",
"price": 13,
"oemnumber": "d1A"
}, {
"category": "B",
"price": 15,
"oemnumber": "d1B"
}, {
"category": "C",
"price": 12,
"oemnumber": "d1C"
}]
fetch();
function fetch() {
/* var data = new FormData();
data.append('search', document.getElementById("search").value);
data.append('ajax', 1);
var xhr = new XMLHttpRequest();
// (CHANGE1) USING ONREADYSTATECHNAGE INSTEAD OF ONLOAD
//some codes/..
console.log(results);*/
wrapper = document.getElementById("results");
wrapper.innerHTML = "";
var rows = "";
if (results.length > 0) {
for (i = 0; i < results.length; i++) {
//generate trs
rows += `<tr id=res${[i]}><td>${results[i].category}</td><td>${results[i].oemnumber}</td><td>$<span data-price='${results[i].price}'>${results[i].price}</span>
select discount >>
%70
%60
%50 100%</td></tr>`;
}
wrapper.innerHTML = `<table class="table">
<thead><th>Category</th><th>OEM</th><th>Price</th></thead><tbody>${rows}</tbody></table>`;
//sme other codes,,
}
};
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<div id="results">
</div>

Local Storage Exposed to Users

Are the values of items stored using the localStorage API viewable by users? Just like cookies?
Safari and Chrome's web inspector allows inspection of that data, yes. I'm not sure how other browsers handle it, but it's certainly not something you can depend on not being edited.
You can easily load the items to be displayed to users.
Just loop through all the items in local storage using Javascript as the page loads...
<body>
<script language="JavaScript" type="text/javascript">
var item = "";//array to hold string values for each key value
var key ="";//array to hold string values for each key name
for (i=0;i < localStorage.length;i++) {
var count = 0;
if (key != "" | key != null) { //or matches what you're looking for
item[count] = localStorage.getItem(key);
key(i) = localStorage.key(i);
count += 1;
}
}
function load_table()
{
if (item == "" || item == " " || item == null) {
document.write("<div id=\"list_table\" style=\"display: block;\">");
document.write("<h3>You have no stored items.</h3>");
document.write("</div>");
}
else {
document.write("<div id=\"list_table\" style=\"display: block;\">");
document.write("<h3>Stored Items</h3>");
document.write("<table width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
for (i=0;i < item.length;i++) {
document.write("<tr><td width=\"33%\" valign=\"top\">"+item(i)+"</td>");
document.write("<td width=\"67%\" valign=\"top\" style=\"padding-left: 0px; text-transform: capitalize;\">"+key(i)+"</td></tr>");
}
}
document.write("</table>");
document.write("</div> <!-- end list_table div -->");
} // end if (item != "")
} // end load_table
</script>
In the html, you stick a div that can be shown or hidden, in an appropriate place.
<div id="items_table" style="display: none;">
<script language="JavaScript" type="text/javascript">
//alert("calling load_table");
load_table();
//alert("DONE calling load_table");
</script>
</div>
If they click a link, you can then display items_table that is populated during page load, and hidden. So long as you don't have thousands of items to load, it loads quickly.
I can dig up a link to toggling the display between block and none for the style display property if you like.