How to append event listener script to dynamically created element formed from server side event data - javascript

I've looked through various similar questions but honestly I don't understand any of the answers (such as they are) in order to apply any of them to what I've written. I'd really appreciate some help as I'm extremely new to this and I'm very lost!
I'm attempting to update an existing page used by moderators for clearing created data as it comes in from users. It uses server side events to check for new items, the old version uses regular forms but the page takes too long to refresh after submitting data. I'm trying to use to set the form data in the background without the need for a refresh but I'm struggling to get the event listeners to attach to the visual elements. They are created dynamically from the event source data. Visually the output looks how I want it to, but it's not behaving as it needs to. One response to a similar-ish question said don't trust the console log, it could be working, but it definitely isn't, the process on the receiving end is a copy of the existing form handler for the old page, so I know that works.
I'm unsure if it's a syntax error or if I need a different approach, the errors I'm getting look like this - it can't find the element I want it to attach the script to:
Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
at :1:37
at EventSource.source.onmessage (MOD.php:39)
This is what I've written on the receiving page:
window.onload = function() {
var last = "";
var lastqueue = "";
var source = new EventSource("MOD-data.php");
source.onmessage = function(event) {
var mydata = event.data;
var data = mydata.split('&&');
var upddate = (data[0]);
var queuedata = (data[1]);
if (mydata != last) {
last = mydata;
if (queuedata != lastqueue) {
var Qdata = queuedata.split('||');
for (let i = 0; i < Qdata.length; i++) {
var itmdata = Qdata[i].split('.');
var itmID = "Q" +(itmdata[0]);
var exists = document.getElementById(itmID);
if (exists === null) {
var itmclass = (itmdata[1]);
var itmbg = (itmdata[2]);
var itmwho = (itmdata[3]);
var itmimg = (itmdata[4]);
var rewbox = "<div id=\"" + itmID + "\" class=\"Qitm " + itmclass + "\" style=\"background:#" + itmbg + "\">" + itmwho + "<input type=\"image\" class=\"rew\" name=\"submit\" src=\"/web/rewards/" + itmimg + ".png\"></div>";
document.getElementById("Qcon").innerHTML += "" + rewbox + "";
var newScript = document.createElement("script");
var inlineScript = document.createTextNode("document.getElementById(" + itmID + ").addEventListener(\"click\", Qclear, true);function Qclear() {\$.ajax({type: \"POST\",url: \"MOD-queue-clear.php\", data: \"timestamp=" + itmID + "\"})}");
newScript.append(inlineScript);
document.getElementById(itmID).appendChild(newScript);
};
};
lastqueue = queuedata;
};
document.getElementById("debug").innerHTML = "<!--" + queuedata + "-->";
};
};
};

Related

update spreadsheet in serverside code run from client html javascript not working

I have an html where user requests add and enters data. The javascript in the body of the html calls the server side. I am unable to connect with the sheet either with saved ID or URL in order to add the row.
I cannot update of my spreadsheet despite #Serge insas comment that openById "it means "open for read and write". Am I making a simple mistake or is this impossible. The code initiated from the client side is running in the server.
const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
var sheet = SpreadsheetApp.openById("[ssId]").getSheetByName('Sheet1');
const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
var sheet = SpreadsheetApp.openById("ssId").getSheetByName('Sheet1');
Both get Error: Exception: Unexpected error while getting the method or property openById on object SpreadsheetApp.
const ssUrl = PropertiesService.getScriptProperties().getProperty('ssUrl');
var sheet = SpreadsheetApp.openByUrl("ssUrl").getSheetByName('Sheet1');
Gets error: Exception: Invalid argument: url
ABOVE IS THE IMPORTANT PART
/**
* this code is run from the javascript in the html dialog
*/
function addMbrCode(myAddForm) {
// removed logging
console.log("Beginning addMbrCode" );
paragraph = body.appendParagraph('Beginning addMbrCode.');
// Exception: Unexpected error while getting the method or property openById on object SpreadsheetApp.
// const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
// var sheet = SpreadsheetApp.openById("[ssId]").getSheetByName('Sheet1');
// var sheet = SpreadsheetApp.openById("ssId").getSheetByName('Sheet1');
// Exception: Invalid argument: url
const ssUrl = PropertiesService.getScriptProperties().getProperty('ssUrl');
var sheet = SpreadsheetApp.openByUrl("ssUrl").getSheetByName('Sheet1');
myAddForm = [ fName, lName, inEmail, fallNum, winNum, sprNum];
var fName = myAddForm[0];
var lName = myAddForm[1];
var inEmail = myAddForm[2];
var fallNum = myAddForm[3];
var winNum = myAddForm[4];
var sprNum = myAddForm[5];
var retCd = '';
/**
* 10 - successful add
* 20 - duplicate - not added
*/
var combNameRng = sheet.getRange(2, 4, numRows).getValues();
var inCName = (fName + '.' + lName).toString().toLowerCase();
if (combNameRng.indexOf(inCName) > 0 ) {
console.log("Alert: Not adding duplicate "
+ fName + ' ' + lName + " retCd: " + 20 );
paragraph = body.appendParagraph("Not adding duplicate "
+ fName + ' ' + lName + " retCd: " + 20);
retCd = 20;
return retCd;
}
sheet.appendRow([fName.toString().toLowerCase()
, lName.toString().toLowerCase()
,
, inEmail.toString().toLowerCase()
]);
const currRow = sheet.getLastRow().toString();
);
retCd = 10;
return retCd;
}
If this makes a difference, here is the javascript from the body of my html in the dialog window.
<script>
document.querySelector("#myAddForm").addEventListener("submit",
function(e)
{
alert('begin addEventListener');
e.preventDefault(); //stop form from submitting
var retCd = google.script.run.addMbrCode(this); // client side validation
document.getElementById('errMsg').textContent = 'Successful member
return false; // do not submit - redisplay html
}
);
</script>
Removed unneeded coding detail
Per #iansedano I created an object/array to use instead of this and added the successhandler and failurehandler. In either case I want to see the html again with my message. This is the current script. Response is so doggy I am not seeing alerts, Logger.log, or console.log. Crazy shoppers using my internet!
<script>
document.querySelector("#myRmvForm").addEventListener("submit",
function(e)
// removed alerts and logging
// removed client side validation for simplicity
cSideValidate();
// Then we prevent the form from being submitted by canceling the event
event.preventDefault();
});
function cSideValidate() {
dataObj = [
document.getElementById('fName').value,
document.getElementById('lName').value,
document.getElementById('email').value
];
var retCd = google.script.run.withSuccessHandler(serverReply)
.withFailureHandler(serverReply)
.rmvMbrCode(dataObj); // server side validation
}
function serverReply {
// logic to set the correct message - this is an example
document.getElementById('errMsg').textContent
= 'Successful delete using email.';
}
</script>
Nothing is being added to my spreadsheet so the server side code is not working. I see my loggin so I know it is getting there.
You're getting ssId from the script properties and assigning it to the ssId variable, but then you pass a string ("ssId") to the openById() function, not the value of the variable.
Try the following please:
const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
var sheet = SpreadsheetApp.openById(ssId).getSheetByName('Sheet1');

Javascript not working even when placed at end of Body section

I have placed the following script at the very end of the body section of my html file where select_1, select_2, select_3 are dynamic (dependent) drop-down lists. (Let's call the script below NEW SCRIPT).
function json(response) {
return response.json()
}
function url_parser(XArray, some_str){
var url_str = [];
for (var i=0; i < XArray.length; ++i)
url_str.push('/'+XArray[i].value);
url_str.push(some_str);
url_str = url_str.join('');
return url_str
}
function selectionHTML(nextFormElement, data){
let optionHTML = '';
for (let tmp of data.select_array) {
optionHTML += '<option value="' + tmp.gid + '">' + tmp.gid + '</option>';
}
nextFormElement.innerHTML = optionHTML;
}
function myChoices(XArray, some_str, nextFormElement){
my_url = url_parser(XArray, some_str);
fetch(my_url).then(json).then(selectionHTML.bind(null, nextFormElement));
}
let select_1 = document.getElementById('tipo_via');
let select_2 = document.getElementById('numero_via');
let select_3 = document.getElementById('apendice_via');
select_1.onchange = myChoices([tipo_via], '/numero_via', select_2);
select_2.onchange = myChoices([tipo_via, numero_via], '/apendice_v', select_3);
However, this script does not work. The only way it work is to:
remove it from the html file and
execute it in the console in a certain order
Providing details in the order of execution:
Execute (in console) all code between function json(response) and let select_3 = document.getElementById('apendice_via');
change the value (in the page) of select_1.
Execute (in console) select_1.onchange = myChoices([tipo_via], '/numero_via', select_2); (select_2 now correctly updates).
change the value (in the page) of select_2.
Execute (in console) select_2.onchange = myChoices([tipo_via, numero_via], '/apendice_v', select_3); (select_3 now correctly updates).
After looking at similar problems, I thought the problem was (maybe still is) related to the timing of the page loading which is why I placed the script at the end of <body>. I have also tried by placing it in the <head>
The NEW SCRIPT was meant to replace the OLD SCRIPT (below ... which works perfectly) as there will be 8 drop-down lists in total.
function json(response) {
return response.json()
}
function url_parser(XArray, some_str,){
var url_str = [];
for (var i=0; i < XArray.length; ++i)
url_str.push('/'+XArray[i].value);
url_str.push(some_str);
url_str = url_str.join('');
return url_str
}
let select_1 = document.getElementById('tipo_via');
let select_2 = document.getElementById('numero_via');
let select_3 = document.getElementById('apendice_via');
select_1.onchange = function(){
my_url = url_parser([tipo_via], '/numero_via');
fetch(my_url).then(json).then(function(data) {
let optionHTML = '';
for (let tmp of data.select_array) {
optionHTML += '<option value="' + tmp.gid + '">' + tmp.gid + '</option>';
}
select_2.innerHTML = optionHTML;
});
}
select_2.onchange = function(){
my_url = url_parser([tipo_via, numero_via], '/apendice_v');
fetch(my_url).then(json).then(function(data) {
let optionHTML = '';
for (let tmp of data.select_array) {
optionHTML += '<option value="' + tmp.gid + '">' + tmp.gid + '</option>';
}
select_3.innerHTML = optionHTML;
});
}
What I can’t get my head around is if the problem is really related to page loading, why does it affect the NEW script and not the OLD one.
Any help will be greatly appreciated. Thanks in advance.
Your code is confusing the calling of a function and referencing a function for an event handler. For example, on this line:
select_1.onchange = myChoices([tipo_via], '/numero_via', select_2);
...what this means is to call myChoices with some arguments and assign the return value to onchange. Instead, what you want is for when the onchange event occurs, you want to call myChoices with those arguments. This is more of what you want:
select_1.addEventListener(
'change',
() => myChoices([tipo_via], '/numero_via', select_2));
select_2.addEventListener(
'change',
() => myChoices([tipo_via, numero_via], '/apendice_v', select_3));
I changed assignment to onchange to the preferred addEventListener, but the most important key here is wrapping the calling of your function in a new function so the actual call isn't made right away. Now, it's not until the DOM change event is fired that the arrow function will be called, and within that function you'll have the call to myChoices(...). Notice that the original working code also was referencing a listener function to defer the call until the event occurred.
The DOM may not be ready (HTML fully parsed and DOM tree constructed internally), so you have to execute your code in a DOMContentLoaded event.
If that doesn't solve the problem then post a detailed error description, e.g. errors from the browser console.

Function works as expected when called from function using XMLHttpRequest, but fails when called from a function using EventSource. Why is this?

I'm working on creating a basic messenger using Javascript. I have three functions in a separate js file called loadMessage, messageListener, and displayMessage.
The function loadMessage makes a call to my database for all existing messages, and then calls displayMessages to construct some divs which I use to show the messages I got from the server. These divs are created to appear under each other, with the bottom div being the newly created one showing the latest message.
Once all the messages have been created loadMessage then calls messageListener. This function 'listens' for any new messages which might appear on the database. If any appear then messageListener calls displayMessage. I expect this to create a new div at the bottom of my other divs as before, however when it calls displayMessage the behaviour is completely different than when loadMessage calls displayMessage.
Specifically, it does not create a new div but instead just changes the text in an existing div which appears anywhere within the newly created divs (for example, the div which displays the first message or one somewhere in the middle).
My HTML and PHP files all behave as expected, so I think my issue is somewhere in these three functions.
How can I fix this to behave as expected?
Code:
// Loads chat messages history and listens for upcoming ones.
function loadMessages(userID, contactID) {
contactIDGlobal = contactID;
//load existing messages
var today = new Date();
var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
var param = "userID="+userID+"&contactID="+contactID+"&date="+date;
xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST","Interface-getMessage.php?", true);
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xmlhttp.send(param);
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//retrives a string of all past messages
var messageString = xmlhttp.responseText;
//parse string to get messages.
var parseMessageString = messageString.split('-');
for (var i = 0; parseMessageString[i] !== null && parseMessageString[i] !== ''; i = i+5){
var contactID = parseMessageString[i];
var senderID = parseMessageString[i+1];
var message = parseMessageString[i+2];
var time = parseMessageString[i+3];
var mID = parseMessageString[i+4];
displayMessage(userID, senderID, contactID, message, date, time, mID);
}
}
};
//listen for new messages
messageListener(userID, contactID);
}
function messageListener(userID, contactID){
if(typeof(EventSource)!=="underfined") {
var newMessage = new EventSource("testerfile.php?userID="+userID+"&contactID="+contactID);
newMessage.onmessage = function(event) {
var newMessageData = event.data;
var parseNewMessage = newMessageData.split('-');
//sender ID may be different to the userID due to the way that messages are stored on the server. Received messages have a different sender.
var senderID = parseNewMessage[0];
var contactID = parseNewMessage[1];
var message = parseNewMessage[2];
var date = parseNewMessage[3];
var time = parseNewMessage[4];
var messageID = parseNewMessage[5];
console.log(event.data);
displayMessage(userID, senderID, contactID, message, date, time, messageID);
};
}else {
document.getElementById("messages").innerHTML = "Your browser does not support this";
}
}
// Displays a Message in the UI.
function displayMessage(userID, senderID, contactID, nMessage, date, time, id){
var messageListElement = document.getElementById('messages');
var messageInputElement = document.getElementById('message');
// If an element for this message already exists, then get it
var id = id;
var div = document.getElementById(id);
// If an element for that message does not exists yet we create it.
if (!div) {
var container = document.createElement('div');
if (userID == senderID){
container.innerHTML = SENDER_MESSAGE_TEMPLATE;
}else{
container.innerHTML = MESSAGE_TEMPLATE;
}
div = container.firstChild;
div.setAttribute('id', id);
for (var i = 0; i < messageListElement.children.length; i++) {
var child = messageListElement.children[i];
}
messageListElement.insertBefore(div, child);
}
var messageElement = div.querySelector('.message');
messageElement.textContent = nMessage;
// Replace all line breaks by <br>.
messageElement.innerHTML = messageElement.innerHTML.replace(/\n/g, '<br>');
}
// Template for messages.
var SENDER_MESSAGE_TEMPLATE =
'<div class="sender_message-container">' +
'<div class="message"></div>' +
'</div>';
var MESSAGE_TEMPLATE =
'<div class="message-container">' +
'<div class="message"></div>' +
'</div>';
The problem was coming from the date being returned as y-m-d, and the parser using "-". This mean I was creating a time var which was the month of my date, and the message ID as the day. I made the alteration below to fix this...
var newMessageData = event.data;
var parseNewMessage = newMessageData.split('-');
//sender ID may be different to the userID due to the way that messages are stored on the server. Received messages have a different sender.
var senderID = parseNewMessage[0];
var contactID = parseNewMessage[1];
var message = parseNewMessage[2];
var date = parseNewMessage[3]+"-"+parseNewMessage[4]+"-"+parseNewMessage[5];
var time = parseNewMessage[6];
var messageID = parseNewMessage[7];

HTTP input to javascript variable remains undefined

Okay, so I'm fairly new to javascript and I'm working on the front-end of a webapplication for my internship, and this just utterly baffles me.
I have a select and an input element, both of which feed into a message model for a database Procedure Call through .POST(). I've done this in other scripts in the project, but here it won't work no matter how I try to assign the values.
Here's the code:
var cctOldSelect = document.getElementById("ConceptToCopy");
var cctOld = cctOldSelect.options[cctOldSelect.selectedIndex].value;
var cctNew = document.getElementById('NewConceptName').value;
console.log("cctOld: " + cctOld + "; cctNew: " + cctNew);
if (cctOld !== "" && cctNew !== "") {
console.log("model creation started.");
var model = {
p_cct_code_old: cctOldSelect.options[cctOldSelect.selectedIndex].value,
p_cct_code_new: document.getElementById('NewConceptName').value,
p_specialty_layout: null
};
console.log("model creation ended.");
}
console.log("cctOld model: " + model.cctOld + "; cctNew model: " + model.cctNew);
output:
cctOld: 1020H; cctNew: 1021H
model creation started.
model creation ended.
cctOld model: undefined; cctNew model: undefined
I've tried multiple ways:
p_cct_code_Old: $scope.<ElementID> //both with and without .value
p_cct_code_Old: document.getElementById(<ElementID>)
var cctOld = document.getElementById(<ElementID>); p_cctOld: cctOld
None of which work. Why won't it assign a value?
The problem in this case is that you create a new object called "model" and with two attributes called "p_cct_code_old" and "p_cct_code_new", then you are trying to access two attributes called "cctOld" and "cctNew" out of scope.
The JS interpreter when you are trying to access a non existing attributes do not throw an error, instead he return an undefined value.
The thing that I suggest to make the things work is:
var model = {};
var cctOldSelect = document.getElementById("ConceptToCopy");
var cctOld = cctOldSelect.options[cctOldSelect.selectedIndex].value;
var cctNew = document.getElementById('NewConceptName').value;
console.log("cctOld: " + cctOld + "; cctNew: " + cctNew);
if (cctOld !== "" && cctNew !== "") {
console.log("model creation started.");
model.p_cct_code_old = cctOldSelect.options[cctOldSelect.selectedIndex].value;
model.p_cct_code_new = document.getElementById('NewConceptName').value;
model.p_specialty_layout: null;
console.log("model creation ended.");
}
console.log("cctOld model: " + model.p_cct_code_old + "; cctNew model: " + model.p_cct_code_new);
Tell me if this solution helped you !

Parse join query using Javascript pointer

I have a two parse objects 'Content' and 'Detail'
Below I have added a screen shot of both classes in their data browsers.
I have a column in 'Content' called descId which is the Detail objects objectId and is a pointer to the 'Detail' Class. I need to know how to obtain data from both classes. This is my attempt but var Details is undefined.
var iBeacon = Parse.Object.extend('Details');
var content = Parse.Object.extend('Content');
var query = new Parse.Query(iBeacon);
query.include('descId');
query.find({
success: function(results) {
// // Do something with the returned Parse.Object values
for (var i = 0; i < results.length; i++) {
var object = results[i];
var Details = object.get('Details');
(function($) {
$('#iBeaconTable').append('<tr><td id = "uuid" >' + object.get('uuid') + '</td><td = "proxId">' + object.get('proximity') + '</td><td = "offerId">' + object.get('offer') + '</td><td class = "buttonRow"><Button id = "btnEdit" onclick = "openDialog()" class = "flat-button">Edit</Button><Button id = "btnDelete" onclick = "Delete()" class = "flat-button">Delete</Button></td></tr>');//<Button id = "editSave">Save</Button></td></tr>');
})(jQuery);
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
I am pulling my hair out on this and I really need dig out...regards.
You are causing yourself confusion by naming the property on the Content class descId. The fact that Parse internally uses a pointer structure with an ID is an implementation detail you don't need to worry about, as far as your code is concerned you'll be using a full Detail object all the time.
The actual error in your code is this line:
var Details = object.get('Details');
Change it to:
// should be using the property name
var Details = object.get('descId');

Categories