Why are remove() and removeChild() not working - javascript

Well I don't exactly understand if its the functions or the flow of my program. I'm querying a simple API for practice which fetches the longitude and latitude of ISS.I wanted to do it every second that's why I used setInterval(). Now I want that when the next data comes the current one automatically removes/disappear and the only the displays, that's why I tried using remove() and removeChild() both but its not removing the element and the new data keeps appearing one below the other. Here's the javascript:
const url = "https://api.wheretheiss.at/v1/satellites/25544";
const box = document.getElementById('div');
function setup(){
let request = new XMLHttpRequest();
request.onload = gotData;
request.open('GET',url);
request.send();
}
setInterval(setup,1000);
function gotData(){
data = JSON.parse(this.responseText);
const box_lat = document.createElement('H2');
const box_long = document.createElement('H2');
box_lat.innerText = "Latitude: " + data.latitude;
box_long.innerText = "Longitude: " + data.longitude;
document.body.appendChild(box);
if(box.hasChildNodes()){
box.removeChild(box_lat);
box.removeChild(box_long);
}else{
box.appendChild(box_lat);
box.appendChild(box_long);
}
}
This gives the following error:---
Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
at XMLHttpRequest.gotData (http://127.0.0.1:3000/Projects/CSS_JS/JavaScript/iss.js:30:9)
I also tried using box.childNodes.length > 0 in the if condition but still got the same result.
Using document.body.removeChild(box) to remove the complete box item never shows that div element!
All I want is that data should update automatically without repeating!

box.removeChild(box_lat);
You are trying to remove box_lat from box.
const box_lat = document.createElement('H2');
but you just created box_lat a few lines before and haven't appended it anywhere.
It sounds like you are trying to access the box_lat variable from the previous invoke of the function … but that's a different variable with a different value and you don't have access to it here.
You need to find the elements you actually want to remove, e.g.:
box.querySelector("h2").remove()

Related

Storing (or passing) an Element/TableCell reference during a server-client round-trip

I'm trying to make a modal popup that does some work on a given table cell within a Google Doc using HTMLService and GAS.
The popup works and in general I can pass data from the server script to the client script and back. The piece of data I can't seem to pass is which table cell the cursor was at when the popup was opened.
Approach 1: If I pass it directly to client and back, the reference is broken, because it's null by the time it gets to the client. I can pass the cell's contents just fine in the exact same context, so strings work, it's just the fact that it's a cell reference. (Makes sense enough.)
Approach 2: If I store the TableCell reference in a global variable on the server side, I get Cannot call method "getText" of undefined--the reference is undefined after the first round trip. (I guess the server script gets reloaded entirely in that time.)
Approach 3: If I use CacheService.getUserCache() on the client side, when I try to get my cached TableCell object, it seems disconnected from the one I had set, because I get the heretofore unGoogleable error Cannot find function getText in object TableCell.
// server, before popup is opened:
cache.put('cell_currently_being_edited', active_doc.getCursor().getElement().getParent());
// popup client calls server function like so:
google.script.run.withSuccessHandler(load_content).get_starting_content();
// server, where the above error occurs:
function get_starting_content() {
var cell_currently_being_edited = cache.get('cell_currently_being_edited');
return cell_currently_being_edited.getText();
}
If cells had some sort of fixed ID value I could pass, that could work...
I would do it based on the text in the cell, but I hope that's not the only option, because cell text in general (and yes, in this specific context too) may not be unique, so after the round trip I may end up replacing text in some other cell than the cursor was in when the user activates the popup.
Solution:
You could use the cell index as unique identifier to pass the RC string back and forth between client and server as string values are legal.
Use Properties/Cache Service to store RC Index information as string for future retrieval in Server, If needed.
Sample Script:
function getRCTIndexOfTable(tableCell) {
var tableRow = tableCell.getParentRow();
var table = tableRow.getParentTable();
var column = tableRow.getChildIndex(tableCell);
var row = table.getChildIndex(tableRow);
var body = table.getParent().asBody();
var tableId = body.getChildIndex(table);
return 'R' + row + 'C' + column + 'T' + tableId;
}
function main() {
var active_doc = DocumentApp.getActiveDocument();
var activeTableCell = active_doc
.getCursor()
.getElement()
.getParent()
.asTableCell();
Logger.log(getRCTIndexOfTable(activeTableCell));
}
References:
TableCell
TableCell from RCIndex
Legal Values
PropertiesService
CacheService
Since the other answer (even after modifying the code) focused on the "saving" half of the problem, I thought I would share code that includes how to load the reference afterwards to complete the round trip:
// save
var table_cell = active_doc.getCursor().getElement().getParent();
var table_row = table_cell.getParentRow();
var table = table_row.getParentTable();
var column_index = table_row.getChildIndex(table_cell);
var table_index = active_doc.getBody().getChildIndex(table);
var row_index = table.getChildIndex(table_row);
cache.put('table_currently_being_edited', table_index);
cache.put('row_currently_being_edited', row_index);
cache.put('column_currently_being_edited', column_index);
// load
function get_cell_currently_being_edited() {
var table_index = cache.get('table_currently_being_edited');
var row_index = cache.get('row_currently_being_edited');
var column_index = cache.get('column_currently_being_edited');
return active_doc.getBody().getChild(table_index).getCell(row_index, column_index);
}

How do I create a reuseable AJAX call function and extract different values from the returned object

I have a Nodejs application that displays randomly generated data on an html page. There are 8 fields each populated from the values of an object with a page load.
I'd like to allow the user to change just a single field with the click of a button to generate a new random result in that field only.
I am using an AJAX calling function but having trouble making the function reusable for each field without rewriting mostly the exact same code 8 times.
I realize the final function will need some error handling as well.
function ajaxCall() {
var http = new XMLHttpRequest();
var data;
http.open("GET", "/randomStuff", true);
http.onload = function() {
// response is an object of 8 different properties
data = JSON.parse(http.response);
// this .log yields the object to the console without any errors
// so I know it works and the correct data is there
console.log(data);
// This works of course but I want to simply call the function for each....
// ... section and put a single value in each.
// Without having to rewrite the entire ajaxCall function each time.
insertP(data.propOne, 'sectionOneID')
}
http.send();
};
// This is the function that puts the data on the page.
function insertP(str, id) {
var p = document.getElementById(id);
p.textContent = str;
};
Using insertP or another as a callback seems to be the solution but I am not sure where to put it. In that case I could simply call the desired object property in the html.

Detecting net::ERR_FILE_NOT_FOUND with only JS

I have recreated a blueprint, which has 60+ rooms, as an inline SVG.
There are functions that display information, such as pictures, when you select or hover a room. I'm using one div container to display the pictures by setting its background property to url('path-of-image.ext'), as can be seen below.
var cla = document.getElementsByClassName('cla');
for (i = 0; i < cla.length; i++) {
cla[i].addEventListener('mouseenter', fun);
}
function fun(){
var str = 'url("media/' + this.id.slice(4) + '.jpg")';
pictureFrame.style.background = str;
pictureFrame.style.backgroundSize = 'cover';
pictureFrame.style.backgroundPosition = 'center'
}
The reason I'm not using the background property's shorthand is because I plan on animating the background-position property with a transition.
However, not all rooms have pictures. Hence console throws the following error, GET ... net::ERR_FILE_NOT_FOUND, when you select or hover said rooms. The error doesn't cause the script to break, but I would prefer not to run that code every single time a room is hovered, even when a given room doesn't have pictures.
Even though I know this can be done imperatively with if/else statements, I'm trying to do this programmatically since there are so many individual rooms.
I've tried using try/catch, but this doesn't seem to detect this sort of error.
Any ideas?
Is it even possible to detect this kind of error?
You could attempt to read it using FileReader and catch/handle NotFoundError error.
If it were to error, you could assign it to an object or array which you would first check upon hover. If the file was in that array, you could avoid attempting to read it again and just handle however you like.
Here is a good article by Nicholas Zakas on using FileReader
First off I would see if there is a way of checking if the file exists before the document even loads so that you don't make unnecessary requests. If you have a database on the backend which can manage this that would serve you very well in the long term
Since you make it sound like the way you only know a file exists is by requesting it, here's a method that will allow you to try this:
function UrlExists(url)
{
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status!=404;
}
This won't request the image twice because of browser caching. As you can see that method is itself being depricated and overall the best way you can remedy this problem is checking before the page even loads; if you have a database or datastructure of any sort, add a class or property to the element if the image exists or not. Then, in your existing method, you can call something like document.getElementsByClassName('cla-with-image') to get only records that you've determined has an image (much more efficient than trying to load images that don't exist).
If you end up using that UrlExists method, then you can just modify your existing method to be
function fun(){
var url = "media/' + this.id.slice(4) + '.jpg";
if (UrlExists(url)) {
var str = 'url(' + url + ')';
pictureFrame.style.background = str;
pictureFrame.style.backgroundSize = 'cover';
pictureFrame.style.backgroundPosition = 'center'
}
}

How to pass data in a button event handler on a Google script extending a spreadheet?

Extending a Google Spreadsheet, I run a script showing some data in a sidebar. At the bottom I want to add a button to mail the data.
However I can't figure out how the data can be passed from button to handler:
There's no possibility to pass data into the event call;
There's no way to get the button object from the event info, I can merely get the ID, which is useless if I can't get to the uiInstance (created outside of the handler function).
So what's the trick?
You have to add a callback element (possibly a panel wrapping everything you need) to the server handler before assigning it to the button. For example:
function myFunction() {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel();
panel.setId('myPanel').add(
app.createTextBox().setName('boxExample')).add(
app.createListBox().setName('listExample').addItem('A').addItem('B'));
// ↓↓ this is what you need ↓↓
var handler = app.createServerHandler('callbackFunction').addCallbackElement(panel);
var btn = app.createButton(btn, handler);
app.add(panel.add(btn));
//show app...
}
function callbackFunction(e) {
var app = UiApp.getActiveApplication();
app.getElementById('myPanel').add(
app.createLabel(e.parameter.boxExample)).add(
app.createLabel(e.parameter['listExample']));
return app;
}
Using PropertiesService
I found (I don't know how) Google Script offers a data storage service called PropertiesService.
Assuming, as in this case, the data is for user only, I would need to first store the data as such:
var userProperties = PropertiesService.getUserProperties()
userProperties.setProperty("myKey", "myValue")
// Note myValue will be a string, so to store an array,
// you'd need JSON.stringify(myArray)
Then as the button handler is called, the script can retrieve the data easily:
var userProperties = PropertiesService.getUserProperties()
var myValue = userProperties.getProperty("myKey")
Using Hidden Widget
An alternative seems to be the use of a "hidden" widget.
function doGet() {
var app = UiApp.createApplication();
// Note that the name "appState" for callbacks, and the id "hidden" for
// getting a reference to the widget, are not required to be the same.
var hidden = app.createHidden("appState", "0").setId("hidden");
app.add(hidden);
var handler = app.createServerHandler("click").addCallbackElement(hidden);
app.add(app.createButton("click me!", handler));
app.add(app.createLabel("clicked 0 times").setId("label"));
return app;
}
function click(eventInfo) {
var app = UiApp.createApplication();
// We have the value of the hidden field because it was a callback element.
var numClicks = Number(eventInfo.parameter.appState);
numClicks++;
// Just store the number as a string. We could actually store arbitrarily complex data
// here using JSON.stringify() to turn a JavaScript object into a string to store, and
// JSON.parse() to turn the string back into an object.
app.getElementById("hidden").setValue(String(numClicks));
app.getElementById("label").setText("clicked " + numClicks + " times");
return app;
}
(code from linked reference)

copying data from one page to another using phantomJS

I am trying to copy some data from one processed web page into a new one that I want to export. The background is that I need to scrape parts of a page and need to build a new page with parts of the original page.
The problem seems that phantomJs includeJs() and evaluate() methods are sandboxed and I can't see a proper way to import DOM from one page to another.
I have some test code that looks like this, with page being the original and out the new page:
....
var title = page.evaluate(function() {
return title = document.getElementById('fooo').innerHTML;
});
console.log('page title:' + title);
//fs.write('c:/Temp/title.js', "var title = '" + title + "';", 'w');
var out = new WebPage;
out.viewportSize = page.viewportSize;
out.content = '<html><head></head><body><div id="wrapper"></div><p>done</p></body></html>';
out.includeJs('c:/Temp/title.js', function() {
var p = document.createElement('p');
p.appendChild(document.createTextNode(title));
document.getElementById('wrapper').appendChild(p);
});
...
The function in your last includeJs call here won't work - as you note, it's sandboxed, and that means that closures won't work, so title won't be defined. A method of passing variables to page.evaluate is noted as a feature request, but isn't available as of PhantomJS v.1.4.1.
The general way I get around this is by using the Function constructor, which allows you to create a function using a string:
var myVar = {some:"values", I:"want to pass into my page"},
test = new Function("window.myVar = " + JSON.stringify(myVar));
page.evaluate(test);
Now you can evaluate a function like the one you have, referencing myVar in the sandbox, and your data will be available in the client scope.

Categories