I'm trying to use NodeJS to modify an external HTML file (which is located in the same directory). In my index.js file I write:
fs.readFile('index.html', (err,html)=>{
if(err){
throw err;
}
html.body.innerHTML += '<div id = "asdf"></div>';
});
As index.html is a valid document. But it doesn't look to be reading it properly, as I get as an error:
"TypeError: Cannot read property 'innerHTML' of undefined".
I guess that html is not getting anything as body.
How can I do changes in HTML using JavaScript?
Here is an example using node-html-parse
HTML file
<html>
<body>
<div id="fist">yolo</div>
</body>
</html>
And the nodejs
const fs = require('fs');
const parse = require('node-html-parser').parse;
fs.readFile('index.html', 'utf8', (err,html)=>{
if(err){
throw err;
}
const root = parse(html);
const body = root.querySelector('body');
//body.set_content('<div id = "asdf"></div>');
body.appendChild('<div id = "asdf"></div>');
console.log(root.toString()); // This you can write back to file!
});
There might be better solutions than node-html-parser, considering the amount of downloads. For example, htmlparser2 has much more downloads, but it also looks more complex :)
In order to manipulate an html file the way you'd be able to in a browser, you'll first need to parse it.
Perhaps node-html-parser can be of use? (Or if a few milliseconds of parsing are not a concern and you want some more functionality, the JSDOM package is very popular too.)
innerHTML is a function provided after DOM parsing. Here you are using a string, so you can either use a DOM parser to create the structure or you can just use regex to isolate the part you want to replace and append the text.
html.replace("</body>",'<div id = "asdf"></div></body>');
Related
I will be dynamically adding elements to my main index.html using .innerHTML += "<p>example</p>"; However I do not want to store the large html-like string in this .js file, but instead import it from another file example.html
example.html:
<p>example</p>
(It is just a snippet of code and not a standalone html file, but I want to keep the .html extension for linting, autocompletion and general readability)
My attempts:
$(...).load('example.html'): did not work as it replaces of contents of ... with this example instead of appending it
import * from example.html: this and other attempts of importing file failed because of MIME error that text/html cannot be imported
I will be perfectly satisfied with a solution of a method that reads .html as text and returns it as a string (preferably not using AJAX or ES6 as I do not feel confident with them). I would then just use the string in .innerHTML += imported_string; and call it a day.
If I correctly understand what you want to do, you can use FileReader to import the content of a file and convert it to text, for example:
function readFile(event) {
var file = event.target.files[0];
var stream = new FileReader();
stream.onload = function(e) {
var fileContent = e.target.result;
alert(fileContent);
}
stream.readAsText(file);
}
document.getElementById('myFile').addEventListener('change', readFile, false);
<input type="file" accept="html" id="myFile">
The file input is for presentation purposes, you can easily adapt this to your needs.
You should also perform the customary checks, which I ommited for brevity purposes.
Create a fetch request to the file that you want to retrieve. This is, in a basic sense, the same way how a browser would request a html file from the server.
The function below sends a request based on what you input as a file. For example, 'example.html'. It then checks if the request was a success and returns the response as a string. The string can then be appended to your innerHTML.
const getFileAsText = async file => {
const response = await fetch(file);
if (!response.ok) {
throw new Error(`Fetching the HTML file went wrong - ${response.statusText}`);
}
return response.text();
};
You can use it like the example below.
getFileAsText('example.html').then(html => {
document.body.innerHTML += html;
});
I am building a little document parser in node.js. To test, I have a raw HTML file, that is generally downloaded from the real website when the application executes.
I want to extract the first code example from each section of the Console.WriteLine that matches my constraint - it has to be written in C#. To do that, I have this sample XPath:
//*[#id='System_Console_WriteLine_System_String_System_Object_System_Object_System_Object_']/parent::div/following-sibling::div/pre[position()>1]/code[contains(#class,'lang-csharp')]
If I test the XPath online, I get the expected results, which is in this Gist.
In my node.js application, I am using xmldom and xpath to try and parse that exact same information out:
var exampleLookup = `//*[#id='System_Console_WriteLine_System_String_System_Object_System_Object_System_Object_']/parent::div/following-sibling::div/pre[position()>1]/code[contains(#class,'lang-csharp')]`;
var doc = new dom().parseFromString(rawHtmlString, 'text/html');
var sampleNodes = xpath.select(exampleLookup,doc);
This does not return anything, however.
What might be going on here?
This is most likely caused by the default namespace (xmlns="http://www.w3.org/1999/xhtml") in your HTML (XHTML).
Looking at the xpath docs, you should be able to bind the namespace to a prefix using useNamespaces and use the prefix in your xpath (untested)...
var exampleLookup = `//*[#id='System_Console_WriteLine_System_String_System_Object_System_Object_System_Object_']/parent::x:div/following-sibling::x:div/x:pre[position()>1]/x:code[contains(#class,'lang-csharp')]`;
var doc = new dom().parseFromString(rawHtmlString, 'text/html');
var select = xpath.useNamespaces({"x": "http://www.w3.org/1999/xhtml"});
var sampleNodes = xpath.select(exampleLookup,doc);
Instead of binding the namespace to a prefix, you could also use local-name() in your XPath, but I wouldn't recommend it. This is also covered in the docs.
Example...
//*[#id='System_Console_WriteLine_System_String_System_Object_System_Object_System_Object_']/parent::*[local-name()='div']/following-sibling::*[local-name()='div']/*[local-name()='pre'][position()>1]/*[local-name()='code'][contains(#class,'lang-csharp')]
There is a library xpath-html that can help you using XPath to query HTML, with minimal efforts and lines of code.
const fs = require("fs");
const html = fs.readFileSync(`${__dirname}/shopback.html`, "utf8");
const xpath = require("xpath-html");
const node = xpath.fromPageSource(html).findElement("//*[contains(text(), 'with love')]");
console.log(`The matched tag name is "${node.getTagName()}"`);
console.log(`Your full text is "${node.getText()}"`);
How to create PDF Documents in Node.JS.? Is there any better solution to manage templates for different types of PDF creation.
I am using PDFKit to create PDF Documents and this will be server side using Javascript. I can not use HTML to create PDF. It will blob of paragraphs and sections with replacing tags with in.
Does anyone know Node.js has any npm package that can deal templates with paragraphs sections headers.
Something like
getTemplateByID() returns a template that contains sections , headers, paragraphs and then i use to replace appropriate tags within the template.
In my case, I have to get my HTML template from my database (PostgreSQL) stocked as stream. I request the db to get my template and I create a tmp file.
Inside my template, I have AngularJS tags so I compile this template with datas thanks to the 'ng-node-compile' module:
var ngCompile = require('ng-node-compile');
var ngEnvironment = new ngCompile();
var templateHTML = getTemplateById(id);
templateHTML = ngEnvironment.$compile(templateHTML)(datas);
Now I have my compiled template (where you can set your paragraph etc.) and I convert them into PDF thanks to a PhantomJS module 'phantom-html-to-pdf'
var phantomHTML2PDF = require('phantom-html-to-pdf')(options);
phantomHTML2PDF(convertOptions, function (error, pdf) {
if(error) console.log(error);
// Here you have 'pdf.stream.path' which is your tmp PDF file
callback(pdf);
});
Now you have your compiled and converted template (pdf), you can do whatever you want ! :)
Useful links:
https://github.com/MoLow/ng-node-compile
https://github.com/pofider/phantom-html-to-pdf
I hope this help !
I want to append to a file but I want to start appending before </xml> tag end. appears. How can I do that?
var fs = require("fs");
fs.appendFile('somefile.xml', 'Content')
I have sth like this but it insert 'content' at the end of file, and I want it in specific line before specific 'keyword'
This might not be the easiest option, but at least it should get the job done. Haven't tested it, so it might require some changes. It reads the file and when it's done, it creates the new file by removing the </xml> tag, adding the content and then adding the </xml> tag again at the end.
var fs = require('fs');
var xmlFile;
fs.readFile('someFile.xml', function (err, data) {
if (err) throw err;
xmlFile = data;
var newXmlFile = xmlFile.replace('</xml>', '') + 'Content' + '</xml>';
fs.writeFile('someFile.xml', newXmlFile, function (err) {
if (err) throw err;
console.log('Done!');
});
});
A good approach is to split the file into a wrapper file and a body file. The wrapper just contains the outermost tag and an entity reference to the body file. Your program can then append to the end of the body file, whereas anyone reading the file as XML should read the wrapper file. The wrapper file will look something like this:
<!DOCTYPE xml [
<!ENTITY e SYSTEM "body.xml">
]>
<xml>&e;</xml>
Using "xml" as an element name, by the way, is bad practice. All names starting "xml" are reserved for future use by W3C.
This approach may not work if you're reading the XML in a browser. I think that some browsers' XML parsers don't support external entities.
how can i append data to a file using javascript?
i tried to use this code, but i got an error:
var fso = new ActiveXObject("Scripting.FileSystemOject");
var filepath = fso.GetFile("member.txt");
var fileObject = fso.OpenTextFile(filepath, 8);
file.WriteLine(id + "|" + pass);
fileObject.close();
the error is on var fso = new ActiveXObject("Scripting.FileSystemOject");, written: Error: Automation server can't create object
is there any other way to append the file using javascript or the way to fix this? thanks :)
EDIT:
i have doing what's written on this, and it still not working :/
I just realized these in your code:
var fileObject = fso.OpenTextFile(filepath, 8,true);
You'll need the true-argument, if the file does not exist, or you want to overwrite/append it.
var filepath = fso.GetFile("member.txt");// This won't work.
var filepath = "your_filePath"; // Use this instead
var fileObject = fso.OpenTextFile(filepath, 8, true);
OpenTextFile() needs a path as a string like "D:/test/file.txt". GetFile() returns an object, which you can see as a string (D:\test\file.txt), but it's not a string. Use also absolute paths, relative paths don't seem to work by my experience.
EDIT
Add the code below to the <head>-part of your html-file, then save locally as a hta (with file extension hta, not htm or html).
<hta:application
applicationName="MyApp"
id="myapp"
singleInstance="yes"
/>
Then run the hta-file. If you still getting an ActiveX-error, it's not supported by your OS. If this works, you haven't done all the security settings correct.
EDIT II
In this case it's not very usefull to get the path through ActiveX, you'll need to write it literal anyway. And I'm not supposed to do your homeworks, but this does the trick...
var filepath = new String(fso.GetFile("member.txt")).replace(/\\/g,'/');
And don't forget what I've said above about using absolute paths...
The 8 in the OpenTextFile function specify that you want to append to the file. Your problem comes from the security restriction of your browser. To make it work you'll have to lower the security level, which is not really recommended.
The error is thrown because there are security restrictions which donot allow the activex to run. change your security settings to allow the activex if your using internet explorer (which i think you are).
This might be useful http://windows.microsoft.com/en-US/windows/help/genuine/ie-activex
Cheers
EDIT: i have doing what's written on this, and it still not working :/
* try Restarting your browser
As pointed out in this comment
Javascript: how to append data to a file
the cause of the error Error: Automation server can't create object is the typo in the progid passed to ActiveXObject: Oject instead of Object:
var fso = new ActiveXObject("Scripting.FileSystemOject");
there is a missing b!