I'm new to node and am practicing making http requests using the request module. In my script, when the user presses a button I want its callback function to make a request which gets the HTML from a webpage and filters it to get an array of data.
My server request works by itself, but when I try to combine it with HTML nothing seems to happen. My HTML looks like this:
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="test1.css" />
<script src = "posts.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
</head>
<body>
<p id = "myText">Hello, this is dog</p>
<button onclick="getPosts()">Get Posts</button>
</body>
</html>
and posts.js is this:
var request = require('request');
function getPosts(){
alert('Hello');
var matches = [];
request('https://www.reddit.com/r/TagPro/top/?sort=top&t=all', function (error, response, body) {
// Handle errors properly.
if (error || response.statusCode !== 200) {
return res.writeHead(error ? 500 : response.statusCode);
}
// Accumulate the matches.
var re = /tabindex="1" >(.+?)</g;
var match;
while (match = re.exec(body)) {
matches[matches.length] = match[1];
}
$("#myText").text(JSON.stringify(matches));
});
}
On the button press, "Hello" gets alerted but nothing happens after that it seems. Is this the proper way to link up node with front end or am I approaching this the wrong way?
If you're running this in the browser then the problem is that you cannot use Node packages in the browser without some extra tooling.
If you check your console, you'll probably see something about "require" being undefined.
You should either read up on how to use tooling like Webpack (or Browserify) to make your Node packages available in the browser.
If you want to stay simple, don't use the Node requests library for client-side (browser) code. Just read up on how to make regular Ajax requests using jQuery or the native XMLHttpRequest API.
You can just replace your request call with something like
$.get('http://someurl.com', function (data) { // stuff });
Related
What I know
As we all know in HTML files we usually use
<head>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
to link an external css stylesheet to a html file so this last one will be formatted as we need and also we use
<head>
<script src="somescript.js"></script>
</head>
to make our html file use an external script.
Question
Is it possible to use the same approach by linking into a Viewer.html file an external file (or even more than one) to load from a simple database saved for example as csv, txt, db, json, xml, and so on?
HTML Pseudo code Example:
<head>
<database src="somedata.db"></database>
</head>
Of course, once the data is available to the html file, a js will be used to put it where it has to go, for example into a table contained into the Viewer.html file.
Punctualizzations:
No server of any kind must be involved, just only local files approach.
No frameworks (no jquery, no Node...)
I'm looking an approach that makes use just of html (HTML 5) + javascript (ES6) and the db file (*.csv, *.txt, *.json, *.xml, *.db, ...) containing only utf8 text. The records and fields in it will follow my specifics:
text field 1|text field 2|text field...|text field N
text field 1|text field 2|text field...|text field N
text field 1|text field 2|text field...|text field N
where the pipe symbol | is my custom field separator and the newline is the record separator.
Try this solution if it feel your needs. It make use of json format:
index.html:
<html>
<head>
<script id="jsonDatas" type="application/javascript" src="./datas.json"></script>
<script src="./index.js"></script>
</head>
<body onload="javascript:checkDatas();">
<div id="datas"></div>
</body>
</html>
datas.json:
let datas = [
{"firstname":"alain","lastname":"deseine"},
{"firstname":"robert","lastname":"dupont"},
{"firstname":"john","lastname":"query"},
{"firstname":"albert","lastname":"dumoulin"},
{"firstname":"bob","lastname":"thesponge"}
];
index.js:
function checkDatas(){
console.log('%o', datas);
}
With this solution you will have access of datas in local file datas.json.
UPDATE
This is the only way to achieve what you describe with local files.
Adding let datas... to datas.json is mandatory to make browser loading datas in a javascript var that you can access later. Browsers will blocks every attempt to access src loaded content for security reasons.
You can also use XMLHttpRequest or fetch to achieve what you want, but for this you need to serve your files with an HTTP server (which can be local). HTTP Server is required because file:// protocol is not supported by CORS. And CORS will block every requests originating from another server accordingly to CORS HEADERs that are set.
To bypass CORS with chrome browser, you can start chrome with thiese flags: --disable-web-security --user-data-dir see for details: Disable same origin policy in Chrome There is aloso some chrome extensions that block CORS (not tested). But ATTENTION, these solutions need user actions and they dramatically restrict browser security.
You can load the data using prefetch/preload in the link.
<link id="user_data" rel="preload" as="fetch" crossorigin="anonymous" href="user-data.json">
But your browser will not take any action on it and you can't access the loaded data in the javascript context.
In order to use the data in the javascript context, you have to call the fetch() function with the link URL. This will not load the data twice since the data is already preloaded on the page load.
function readData(selector){
const link = document.querySelector(selector)
if(link){
return fetch(link.href)
.then( res => res.json()) // use res.text() for XML or CSV
}else{
throw 'Invalid selector on loadData(selector)'
}
};
function abc(){
//...
readData('#user_data').then(data => {
console.log(data)
})
//...
}
Alternative solution
You can define your own custom element to get the desired feature
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="data-loader.js"> </script>
<!-- this script must be called before the custom element -->
<data-loader id="json" src="data.json" onload="onJSONFetch"></data-loader>
<script src="script.js"> </script>
</head>
<body>
<data-loader id="phone-xml" src="phone-number.xml" onload="onXMLFetch(event)"></data-loader>
<data-loader id="user-data" src="user.csv"></data-loader>
</body>
</html>
data-loader.js
class DataLoader extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.load();
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'src') {
this.load();
}
}
load() {
const attr = this.getAttribute('src');
if (attr) {
fetch(attr).then(res => res.text()).then((data) => {
const event = new CustomEvent('load', { detail: data })
this.dispatchEvent(event);
const inlineEventHandlerName = this.getAttribute('onload');
const cb = inlineEventHandlerName && window[inlineEventHandlerName];
if((typeof cb) === 'function'){
cb(event);
}
})
}
}
}
customElements.define('data-loader', DataLoader);
script.js
const documentReady = () => {
document.querySelector('#user-data').addEventListener('load', function (event) {
console.log('user-data loaded', event.detail);
})
}
function onXMLFetch(event){
console.log('onXMLFetch', event.detail);
}
function onJSONFetch(event){
console.log('onJSONFetch', event.detail);
}
document.onreadystatechange = () => {
if (document.readyState === 'interactive') {
documentReady();
}
}
Thanks to Alaindeseine's answer I got some inspirations and I did this:
into any text file (so no necessarily *.json) I put these records (notice that the following is a template literal that makes use of back ticks `, not single ' or double " quotes):
var db =
`
my data field | my data field | my data field | my data field
my data field | my data field | my data field | my data field
my data field | my data field | my data field | my data field
`
Also because of the use of var instead of let, I have the advantage that the variable db gets hoisted so I can make use of it everywhere into the file and get its content from any other script or function. Also it can be potentially declared and overwritten multiple times after the previous one is elaborated so it will be also possible to queue multiple db files to show them into the same viewer.html, which is a great advantage, for my case at least.
I already tried this way with different text file types and works great. For example I've tested
*.json
*.tsv
*.csv
*.txt
*.db
*.ext
*(no extension at all)
So at this point I believe that this can also handle other generic files extensions, at the condition they contain text enclosed into a template literal, similarly to the above.
Of course into the html file I have to point/link to the database file as if it was a script like the following:
<script src="someDBfile.ext"></script> // This links the database
<script src="myJavascript.js"></script> //This links the javascript functions that get the data from the **db** variable and create the table/s to show the well formatted data contained into the database.
Nevertheless let's hope browser developers will implement in browsers a standard and more semantically correct way of doing this with an appropriate and semantically correct tag as
<database src="somedata.db"></database>
that works similarly.
Also such procedure allows me to have just one Viewer.html to use to show all compatible files, which is also great.
I'm trying to build a application server using scala.js, scalatags, akka-http, and mill as my build tool. All goes well until the browser tries to find scripts with generated scala.js code. This is the scalatags code which successfully gets built and references the compiled scala.js code (HiPage.js - built as a ScalaJSModule in mill). When it is run the println below prints out:
file:/Users/nnovod/projects/lims/LIMS/resources/HiPage.js
which is indeed where I've placed the javascript output from scala.js
object HiPage {
val boot =
"Hi().main(document.getElementById('contents'))"
println(getClass.getResource("/HiPage.js").toString)
val skeleton =
html(
head(
script(`type`:="text/javascript", src:="/HiPage.js"),
link(
rel:="stylesheet",
href:="https://cdnjs.cloudflare.com/ajax/libs/pure/0.5.0/pure-min.css"
)
),
body(
onload:=boot,
div(id:="contents")
)
)
}
This eventually shows up in the browser as follows:
<html>
<head>
<script type="text/javascript" src="/HiPage.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/0.5.0/pure-min.css"/>
</head>
<body onload="Hi().main(document.getElementById('contents'))">
<div id="contents"></div>
</body>
</html>
This is my akka-http route...
val route =
path("hello") {
get {
complete(
HttpEntity(
ContentTypes.`text/html(UTF-8)`,
HiPage.skeleton.render
)
)
}
}
The browser can never find the HiPage.js ("Failed to load resource: the server responded with a status of 404 (Not Found)"). HiPage.js is in a top level resources directory and is found by the println(getClass.getResource("/HiPage.js").toString) in the code. What do I have to do to get it seen when the browser requests it from the server?
Not sure if this is the best way but I finally was able to solve the problem by having all src references in script start with /resource and then editing my akka-http route to include the following:
pathPrefix("resource") {
extractUnmatchedPath { unmatched =>
val resource = unmatched.toString()
if (!resource.startsWith("/"))
reject()
else
getFromResource(resource.substring(1))
}
}
I'm new to Web Development (including JavaScript and HTML) and have a few issues within my personal project that seem to have no clear fixes.
Overview
My project is taking input from a user on the website, and feeding it to my back-end to output a list of word completion suggestions.
For example, input => "bass", then the program would suggest "bassist", "bassa", "bassalia", "bassalian", "bassalan", etc. as possible completions for the pattern "bass" (these are words extracted from an English dictionary text file).
The backend - running on Node JS libraries
trie.js file:
/* code for the trie not fully shown */
var Deque = require("collections/deque"); // to be used somewhere
function add_word_to_trie(word) { ... }
function get_words_matching_pattern(pattern, number_to_get = DEFAULT_FETCH) { ... }
// read in words from English dictionary
var file = require('fs');
const DICTIONARY = 'somefile.txt';
function preprocess() {
file.readFileSync(DICTIONARY, 'utf-8')
.split('\n')
.forEach( (item) => {
add_word_to_trie(item.replace(/\r?\n|\r/g, ""));
});
}
preprocess();
module.exports = get_words_matching_trie;
The frontend
An HTML script that renders the visuals for the website, as well as getting input from the user and passing it onto the backend script for getting possible suggestions. It looks something like this:
index.html script:
<!DOCTYPE HTML>
<html>
<!-- code for formatting website and headers not shown -->
<body>
<script src = "./trie.js">
function get_predicted_text() {
const autofill_options = get_words_matching_pattern(input.value);
/* add the first suggestion we get from the autofill options to the user's input
arbitrary, because I couldn't get this to actually work. Actual version of
autofill would be more sophisticated. */
document.querySelector("input").value += autofill_options[0];
}
</script>
<input placeholder="Enter text..." oninput="get_predicted_text()">
<!-- I get a runtime error here saying that get_predicted_text is not defined -->
</body>
</html>
Errors I get
Firstly, I get the obvious error of 'require()' being undefined on the client-side. This, I fix using browserify.
Secondly, there is the issue of 'fs' not existing on the client-side, for being a node.js module. I have tried running the trie.js file using node and treating it with some server-side code:
function respond_to_user_input() {
fs.readFile('./index.html', null, (err, html) => {
if (err) throw err;
http.createServer( (request, response) => {
response.write(html);
response.end();
}).listen(PORT);
});
respond_to_user_input();
}
With this, I'm not exactly sure how to edit document elements, such as changing input.value in index.html, or calling the oninput event listener within the input field. Also, my CSS formatting script is not called if I invoke the HTML file through node trie.js command in terminal.
This leaves me with the question: is it even possible to run index.html directly (through Google Chrome) and have it use node JS modules when it calls the trie.js script? Can the server-side code I described above with the HTTP module, how can I fix the issues of invoking my external CSS script (which my HTML file sends an href to) and accessing document.querySelector("input") to edit my input field?
I have an HTML page where several JavaScript, CSS and images files are referenced. These references are dynamically injected and user can manually copy the HTML page and the support files to another machine.
If some JS or CSS are missing, the browser complains in the console. For example:
Error GET file:///E:/SSC_Temp/html_005/temp/Support/jquery.js
I need somehow these errors reported back to me on the inline JavaScript of the HTML page so I can ask user to first verify that support files are copied correctly.
There's the window.onerror event which just inform me that there's a JS error on the page such as an Unexpected Syntax error, but this doesn't fire in the event of a 404 Not Found error. I want to check for this condition in case of any resource type, including CSS, JS, and images.
I do not like to use jQuery AJAX to verify that file physically exists - the I/O overhead is expensive for every page load.
The error report has to contain the name of the file missing so I can check if the file is core or optional.
Any Ideas?
To capture all error events on the page, you can use addEventListener with the useCapture argument set to true. The reason window.onerror will not do this is because it uses the bubble event phase, and the error events you want to capture do not bubble.
If you add the following script to your HTML before you load any external content, you should be able to capture all the error events, even when loading offline.
<script type="text/javascript">
window.addEventListener('error', function(e) {
console.log(e);
}, true);
</script>
You can access the element that caused the error through e.target. For example, if you want to know what file did not load on an img tag, you can use e.target.src to get the URL that failed to load.
NOTE: This technically will not detect the error code, it detects if the image failed to load, as it technically behaves the same regardless of the status code. Depending on your setup this would probably be enough, but for example if a 404 is returned with a valid image it will not trigger an error event.
you can use the onload and onerror attributes to detect the error
for example upon loading the following html it gives alert error1 and error2 you can call your own function e.g onerror(logError(this);) and record them in an Array and once the page is fully loaded post is with single Ajax call.
<html>
<head>
<script src="file:///SSC_Temp/html_005/temp/Support/jquery.js" onerror="alert('error1');" onload="alert('load');" type="text/javascript" ></script>
</head>
<body>
<script src="file:///SSC_Temp/html_005/temp/Support/jquery.js" onerror="alert('error2');" onload="alert('load');" type="text/javascript" ></script>
</body>
</html>
I've put together the code below in pure JavaScript, tested, and it works.
All the source code (html, css, and Javascript) + images and example font is here: on github.
The first code block is an object with methods for specific file extensions: html and css.
The second is explained below, but here is a short description.
It does the following:
the function check_file takes 2 arguments: a string path and a callback function.
gets the contents of given path
gets the file extension (ext) of the given path
calls the srcFrom [ext] object method that returns an array of relative paths that was referenced in the string context by src, href, etc.
makes a synchronous call to each of these paths in the paths array
halts on error, and returns the HTTP error message and the path that had a problem, so you can use it for other issues as well, like 403 (forbidden), etc.
For convenience, it resolves to relative path names and does not care about which protocol is used (http or https, either is fine).
It also cleans up the DOM after parsing the CSS.
var srcFrom = // object
{
html:function(str)
{
var prs = new DOMParser();
var obj = prs.parseFromString(str, 'text/html');
var rsl = [], nds;
['data', 'href', 'src'].forEach(function(atr)
{
nds = [].slice.call(obj.querySelectorAll('['+atr+']'));
nds.forEach(function(nde)
{ rsl[rsl.length] = nde.getAttribute(atr); });
});
return rsl;
},
css:function(str)
{
var css = document.createElement('style');
var rsl = [], nds, tmp;
css.id = 'cssTest';
css.innerHTML = str;
document.head.appendChild(css);
css = [].slice.call(document.styleSheets);
for (var idx in css)
{
if (css[idx].ownerNode.id == 'cssTest')
{
[].slice.call(css[idx].cssRules).forEach(function(ssn)
{
['src', 'backgroundImage'].forEach(function(pty)
{
if (ssn.style[pty].length > 0)
{
tmp = ssn.style[pty].slice(4, -1);
tmp = tmp.split(window.location.pathname).join('');
tmp = tmp.split(window.location.origin).join('');
tmp = ((tmp[0] == '/') ? tmp.substr(1) : tmp);
rsl[rsl.length] = tmp;
}
});
});
break;
}
}
css = document.getElementById('cssTest');
css.parentNode.removeChild(css);
return rsl;
}
};
And here is the function that gets the file contents and calls the above object method according to the file extension:
function check_file(url, cbf)
{
var xhr = new XMLHttpRequest();
var uri = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function()
{
var ext = url.split('.').pop();
var lst = srcFrom[ext](this.response);
var rsl = [null, null], nds;
var Break = {};
try
{
lst.forEach(function(tgt)
{
uri.open('GET', tgt, false);
uri.send(null);
if (uri.statusText != 'OK')
{
rsl = [uri.statusText, tgt];
throw Break;
}
});
}
catch(e){}
cbf(rsl[0], rsl[1]);
};
xhr.send(null);
}
To use it, simply call it like this:
var uri = 'htm/stuff.html'; // html example
check_file(uri, function(err, pth)
{
if (err)
{ document.write('Aw Snap! "'+pth+'" is missing !'); }
});
Please feel free to comment and edit as you wish, i did this is a hurry, so it may not be so pretty :)
#alexander-omara gave the solution.
You can even add it in many files but the window handler can/should be added once.
I use the singleton pattern to achieve this:
some_global_object = {
error: (function(){
var activate = false;
return function(enable){
if(!activate){
activate = true;
window.addEventListener('error', function(e){
// maybe extra code here...
// if(e.target.custom_property)
// ...
}, true);
}
return activate;
};
}());
Now, from any context call it as many times you want as the handler is attached only once:
some_global_object.error();
I saw this great API (http://www.dictionaryapi.com/products/api-collegiate-dictionary.htm) by merriam webster that returns an XML file with all the details in it including definitions and pronunciations.
This API requires a key so i registered and got a key for my account.
I am making the request using Javascript(XHR) but the status returned is zero.
Then i googled the error it said that it may be because my request is going from a "file:///" protocol instead of "http://", so i installed LAMP stack on my PC then hosted the file on my localhost server and even then no luck.
Another thread said that i cant make cross domain requests.
Please can you help me. Below is my HTML code from which i call function in my javascript file.
<html>
<head>
<script type="text/javascript" src="context-script.js">
</script>
</head>
<body>
<h1>Merriam Webster</h1>
<div>
<b>To:</b> <span id="to"></span><br />
<b>From:</b> <span id="from"></span><br />
<b>Message:</b> <span id="message"></span><br/>
<b>Sound:</b><span id="sound"></span><br />
</div>
<script>
callOtherDomain();
</script>
</body>
</html>
Below is my JAvascript file context-script.js code:
function callOtherDomain()
{
invocation = new XMLHttpRequest();
var url = 'http://www.dictionaryapi.com/api/v1/references/collegiate/xml/happy?key=8f394b2c-77e8-433d-b599-f3ca87660067';
//url="note.xml";
if(invocation)
{
invocation.open('GET', url, true);
invocation.withCredentials = "true";
invocation.onreadystatechange = handler;
invocation.send();
alert("ref");
}
}
function handler(evtXHR)
{
if (invocation.readyState == 4)
{
alert("erg");
if (invocation.status == 200)
{
var response = invocation.responseXML;
document.getElementById("to").innerHTML=
response.getElementsByTagName("dt")[0].childNodes[0].nodeValue;
document.getElementById("from").innerHTML=
response.getElementsByTagName("dt")[1].childNodes[0].nodeValue;
document.getElementById("message").innerHTML=
response.getElementsByTagName("dt")[2].childNodes[0].nodeValue;
}
else
alert(invocation.status);
}
else
dump("currently the application is at" + invocation.readyState);
}
But when i change the URL to "note.xml" which is locally stored on the localhost code works absolutely fine.
Thanks in advance.
While this question is several years old, I worked with dictionaryapi.com previously and the solution is two-fold:
Your first step to host on a local server was right on (localhost:8000 or http://127.0.0.1:8000). I prefer using the Python SimpleHTTPServer, started in the root directory of the page you're trying to host with whichever CLI tool you're most familiar/comfortable with, py -m http.server.
After that, just complete a jQuery call using ajax, get, or XMLHttpRequest—whichever you prefer. For example:
$.ajax({
url: 'http://www.dictionaryapi.com/api/v1/references/collegiate/xml/[YourWord]?key=[YourKeyHere],
method: "GET"
}).done(function(response){
console.log(response);
});