Creating / downloading a .html file with a chrome extension - javascript

I'm building my first Chrome extension. So far my code takes elements of a webpage and creates HTML markup (loaded in a string in Javascript).
My extension leads in a button
$(".column1").prepend('<div class="actions" style="margin-bottom: 1rem;"><button id="edmMaker">Make an EDM!</a></div>')
$('#edmMaker').click(function(){
var html = "<body><section><h1>Here is some HTML text</h1></section><div><p>Here's some more</p></div></body>"
// create a .html file and download it to the user's desktop
});
In Node.JS I would just write a .html file to the local disk, but I can't quite figure out how this works in Chrome Extension world.
How can I do this?
Sub-question: Is there any way to tabify the HTML that is being output? The actual code I'm outputting is an HTML email template, and Javascript will only let me load in a string without line breaks and tabs.

Here's a method I wrote that leverages HTML5's download attribute to download a file:
var saveHTML = function(fileName, html){
// Escape HTML
var el = document.createElement("dummy");
el.innerText = html;
var escapedHTML = el.innerHTML;
// Use dummy <a /> tag to save
var link = document.createElement("a");
link.download = fileName;
link.href = "data:text/plain,"+escapedHTML;
link.click(); // trigger click/download
};
saveHTML("myHTML.html", "<html></html>");
Check it out in action here.
If you're not looking to save the file, you can just use storage.
EDIT:
As #Xan pointed out below, the chrome.downloads API exists as well which may be of some use, specifically chrome.downloads.download() method.
As for multiline strings with tabs/spaces/newlines, there's 3 ways:
1.) Manually, using newlines (\n) and tabs (\t)
"<body>\n\t<section>\n\t\t<h1>Here is some HTML text</h1>\n\t</section>\n\t<div>\n\t\t<p>Here's some more</p>\n\t</div>\n</body>"
Which comes out to:
<body>
<section>
<h1>Here is some HTML text</h1>
</section>
<div>
<p>Here's some more</p>
</div>
</body>
2.) Using JavaScript's multi-line string support, which requires that you insert a backslash at the end of a line:
var html = "<body>\
<section>\
<h1>Here is some HTML text</h1>\
</section>\
<div>\
<p>Here's some more</p>\
</div>\
</body>";
3.) Array.join:
var html = [
"<body>",
" <section>",
" <h1>Here is some HTML text</h1>",
" </section>",
" <div>",
" <p>Here's some more</p>",
" </div>",
"</body>"
].join("\n");

Related

File size issue in Chrome for base64-encoded PDF data in OBJECT tag

My problem is related to displaying base64-encoded PDF file data in Chrome browser. The PDF files are not static physical files rather they are streams converted to base64-encoded data. It is a single page application using Node.js backend. The pdf file is displayed in a modal popup box, instead of a new tab or window. The code responsible for displaying the base64-encoded PDF data is given below:
var objbuilder = '';
objbuilder += ('<object width="100%" height="100%" data="data:application/pdf;base64,');
objbuilder += (r.response);
objbuilder += ('" type="application/pdf" class="internal">');
objbuilder += ('</object>');
var html = '';
html += '<div class="modal fade in" id="linkedDoc" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">';
html += '<div class="modal-dialog">';
html += '<div class="modal-content" style="height:800px;width:600px;">';
html += '<div class="modal-body" style="height: 100%;" id = "linkedBody">';
html += '<div style="height:25px;clear:both;display:block;">';
html += '<a class="controls previous" href="javascript:void(0);" data-dismiss="modal" aria-hidden="true">Close</a>';
html += '</div></div></div></div></div>';
$('#linkedDoc').remove();
$('body').append(html);
$('#linkedBody').append(objbuilder);
$('.modal').modal({
keyboard: false,
show: true
});
$('.modal-dialog').draggable({
handle: ".modal-header"
});
$('.modal-content').resizable();
$('#linkedBody').on('show.bs.modal', function () {
$(this).find('.modal-body').css({
'max-height': '100%',
'max-width': '100%'
});
});
The approach works well for file with almost 1 MB file size but not more than that. Some of the files I want to display are 5 MBs and some are even 10 MBs. How can I fix this issue. This and This SO questions are relevant to the topic but not specific for my situation.
I can't use HTML 'embed' and 'iframe' tags because they need a physical file as their 'src' attribute but I don't have a physical PDF file rather its in-memory data.
Anyone stumbling upon this question in future, I fixed it using blob:URL technique. THIS StackOverflow question guided me towards the solution. So instead of opening the PDF file in HTML object element, I opened it in a new window using Blob URL. It could not be achieved using data:URL after Chrome version 60 update in which they blocked data URLs.
First Approach
Can you try with iframe tag instead of object tag.
Second Approach
can you provide the link for PDF instead of actual data.
you can create the pdf file and store in some where then after provide the link to object tag.
Third You can try Embed tag also
May be Solve your issue :)

Display images from Local File in html,css & js website gallery

In my university course/module that covers intermediate HTML and CSS, and basic java-script (thought we haven't gotten there yet): I need to create a website using HTML, CSS and optionally java-script as bonus marks.
I am stuck at the gallery, I want to make a responsive image grid (that I can learn/get from https://www.w3schools.com/howto/howto_css_image_grid_responsive.asp); However I want to have a local folder filled with say 100 images and my website with html/css/js code that doesn't require me to manually hard code each individual image from the folder. In hindsight I want to add and remove images from said folder and have the website's gallery adapting to the added/removed images.
Theoretically I assume that I'll need to read in the folder's contents, into a list/array, then somehow parse them and output the content.
I have found two sources that touches on the idea:
- https://www.html5rocks.com/en/tutorials/file/dndfiles/
- https://github.com/blueimp/JavaScript-Load-Image#meta-data-parsing
I have searched for a few hours and I would think that such a code should exist somewhere, thought I believe my lack of knowledge regarding html, css, js, etc and general terminology is hindering me in my search, thus any advice would be greatly appreciated.
Thank you in advance for your time and effort.
Consider using a shell script from the corresponding directory where source image files are present.
You can simply make a .cmd file with the following code and execute that, it would dynamically generate an html file where you can display images as you wish.
scriptToExecute.cmd
echo ^<!doctype html^>^<head^>^</head^>^<body^> >> index.html
for %%j in (*.JFIF, *.png, *.JPG, *.GIF) do echo ^<img src=^"./%%j^" style="width:176px;height:300px" ^> >> index.html
echo ^</body^>^</html^> >> index.html
index.html
<!doctype html><head></head><body>
<img src="./2.jfif" style="width:176px;height:300px" >
<img src="./3.jfif" style="width:176px;height:300px" >
<img src="./4.jfif" style="width:176px;height:300px" >
<img src="./1.png" style="width:176px;height:300px" >
</body></html>
You can make changes to the shell script to display the images in different elements such as a carousel, etc.
If you want to load images from a folder dynamically (not entering each manually) you can't avoid needing javascript. Adding jQuery into the mix makes it easier not harder. Don't be afraid of using jQuery even if you're only just starting to learn javascript.
To be able to use jQuery, all you need to do is add this:
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
Essentially what that does is add the $ variable which you'll see in the following code provides a straightforward way to make an ajax call and also to add new img elements to the body element.
To create an element for each image in the folder (assuming it contains only images) should be as simple as the following:
<script type="text/javascript">
var folder = "images"; //TODO: change this to the path to your folder with the images.
$.ajax({
url: folder,
success: function(data) {
$(data).find("a").attr("href", function(i, val) {
$("body").append("<img src='" + folder + '/' + val + "'>");
});
}
});
</script>
Alternately, if you just want to avoid having to type out all the img elements by hand and fill in each src attribute by hand, you can write a bit of javascript that automates that. Using the following script you'll be able to click 'Choose Files' and select all the images in the folder, click 'Open', and then click 'Go' and it will generate the html for all the img elements and display it. You can then copy that html and manually paste it into your real project.
<input id="file" type="file" multiple />
<button onClick="go()">Go</button>
<div id="output"></div>
<script type="text/javascript">
function go() {
const fileInput = document.getElementById('file');
const outputDiv = document.getElementById('output');
let html = '';
for (const file of fileInput.files) {
html += '<img src="images/' + file.name + '" />';
}
outputDiv.textContent = html;
}
</script>
https://codepen.io/rockysims/pen/MPEMOG

Save HTML element locally in Javascript

My Snippet tool allows users to save/copy code snippets in order to share them. I use html2canvas in order to display the snippet that the user has created in the console and allow them to save it.
But I have found many problems in their display on browsers other than Firefox, as you can see here (it's blurry and the letter spacing is abnormal).
Here's how I save the snippet once the user presses "Save":
// Call html2canvas with the console element
html2canvas(document.getElementsByClassName('console'), {
onrendered: function(canvas) {
var url = canvas.toDataURL()
// Set the element's URL in an img tag
var img = '<img src="' + url + '" style="border:0;"></img>'
// Open a window and display the image in it
var x = window.open();
x.document.open();
x.document.write(img);
x.document.close();
}
});
My best solution at the moment is to have the snippet saved locally and directly to the user, how may I have a specific HTML element downloaded using Javascript?
EDIT:
This is the HTML element that I wish to save, it contains the console design and a codemirror component that represents the text editor in which users type:
<div class="console">
<div class="console-header">
<div class="console-buttons">
<div class="console-button button-1"></div>
<div class="console-button button-2"></div>
<div class="console-button button-3"></div>
</div>
</div>
<div class="console-content">
<codemirror [(ngModel)]="content" [config]="config"></codemirror>
</div>
</div>

PHP saveHTML function is not saving HTML properly

I have been trying to save the source code of a section of a webpage using PHP. When I extract the content of whole webpage, the source code order is preserved but when I try to get part of the document using
$dom = new DOMDocument;
$dom->loadHTML($webpage);
$xpath = new DOMXPath($dom);
$query_tag = "//div[contains(#class, 'class-name')]";
$result = $dom->saveHTML($xpath->query($query_tag)->item(0));
The script tag gets messed up. Until now, this is the only website where this issue occurred. Are there some limitations of saveHTML function that I am not aware of?
This is what I should be receiving:
<div id="sponsored-category-header" class="page-header sponsored-category-header clear"> <script type="text/javascript">jQuery(document).ready(function($) {
var cat_head_params = {"sponsor":"SEO PowerSuite","sponsor_logo":"https:\/\/www.searchenginejournal.com\/wp-content\/plugins\/abm-sej\/includes\/category-images\/SPS_128.png","sponsor_text":"<div class=\"taxonomy-description\">Dominate Google local search results with ease! Get your copy of SEO PowerSuite and keep <a rel=\"nofollow\" href=\"http:\/\/sejr.nl\/PowerSuite-2016-5\" onClick=\"__gaTracker('send', 'event', 'Sponsored Category Click Var 1', 'Local Search', 'SEO PowerSuite');\" target=\"_blank\">your local SEO strategy<\/a> up to par.<\/div>","logo_url":"http:\/\/sejr.nl\/PowerSuite-2016-5","ga_labels":["Local Search","SEO PowerSuite"]}
$('#sponsored-category-header').append('<div class="sponsored-category-logo"></div>');
$('#sponsored-category-header .sponsored-category-logo').append(' <a rel="nofollow" href="'+cat_head_params.logo_url+'" onClick="__gaTracker(\'send\', \'event\', \'Sponsored Category Click Var 1\', \''+cat_head_params.ga_labels[0]+'\', \''+cat_head_params.ga_labels[0]+'\');" target="_blank"><img class="nopin" src="'+cat_head_params.sponsor_logo+'" width="96" height="96" /></a>');
$('#sponsored-category-header').append('<div class="sponsored-category-details"></div>');
$('#sponsored-category-header .sponsored-category-details').append('<h3 class="page-title sponsored-category-title">'+cat_head_params.sponsor+'</h3>');
$('#sponsored-category-header .sponsored-category-details').append(cat_head_params.sponsor_text);
});</script> </div>
This is what I actually get:
<div id="sponsored-category-header" class="page-header sponsored-category-header clear"> <script type="text/javascript">jQuery(document).ready(function($) {
var cat_head_params = {"sponsor":"SEO PowerSuite","sponsor_logo":"https:\/\/www.searchenginejournal.com\/wp-content\/plugins\/abm-sej\/includes\/category-images\/SPS_128.png","sponsor_text":"<div class=\"taxonomy-description\">Dominate Google local search results with ease! Get your copy of SEO PowerSuite and keep <a rel=\"nofollow\" href=\"http:\/\/sejr.nl\/PowerSuite-2016-5\" onClick=\"__gaTracker('send', 'event', 'Sponsored Category Click Var 1', 'Local Search', 'SEO PowerSuite');\" target=\"_blank\">your local SEO strategy<\/a> up to par.<\/div>","logo_url":"http:\/\/sejr.nl\/PowerSuite-2016-5","ga_labels":["Local Search","SEO PowerSuite"]}
$('#sponsored-category-header').append('<div class="sponsored-category-logo"></script>
</div>');
$('#sponsored-category-header .sponsored-category-logo').append(' <a rel="nofollow" href="'+cat_head_params.logo_url+'" onclick="__gaTracker(\'send\', \'event\', \'Sponsored Category Click Var 1\', \''+cat_head_params.ga_labels[0]+'\', \''+cat_head_params.ga_labels[0]+'\');" target="_blank"><img class="nopin" src="'+cat_head_params.sponsor_logo+'" width="96" height="96"></a>');
$('#sponsored-category-header').append('<div class="sponsored-category-details"></div>');
$('#sponsored-category-header .sponsored-category-details').append('<h3 class="page-title sponsored-category-title">'+cat_head_params.sponsor+'</h3>');
$('#sponsored-category-header .sponsored-category-details').append(cat_head_params.sponsor_text);
}); </div>
In case you missed it, the ending script tag has moved up a few lines.
Just to be clear, I am not talking about rendered HTML. I am talking about the actual source code that I get after making the request. Any help on how to resolve this issue will be appreciated.
I know that the function saveHTML is causing the issue because when I echo the whole page through PHP, every tag is in the right place.
First of all, your code should be triggering a good bunch of warnings like these:
Warning: DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in
Entity Warning: DOMDocument::loadHTML(): Unexpected end tag :
strong in Entity Warning: DOMDocument::loadHTML(): Tag header
invalid in Entity
This is to expect with on-the-wild HTML (and this page's code is nor particularly bad) but you haven't even mentioned it, what makes me suspect that you might not have error reporting enabled in your development box.
Additionally, the page has huge amounts of JavaScript and DOMDocument is just an HTML parser.
With that, we can get a clear picture of what's happening. Since DOMDocument is not a full-fledged browser it doesn't understand JavaScript code. That means that it detects the <script> tag but it doesn't handle its contents as JavaScript—it merely looks for a closing tag and the first one he finds is this:
$('#sponsored-category-header').append('<div class="sponsored-category-logo"></div>');
^^^^^^
It doesn't know that it's a JavaScript string and should be ignored. Instead, it thinks the wrong tag is being closed so it attempts to fix what's technically invalid HTML and adds the missing </script> tag.
For this precise reason, the <script>...</script> tag set has traditionally been written this way:
<script type="text/javascript"><!--
var foo = '<p>Escaped end tag<\/p>';
//--></script>
... so user agents that are unaware of JavaScript can safely ignore the whole tag (hey, it's nothing but a good old HTML comment). However, nowadays it's almost universally considered bad practice because "all browsers understand JavaScript".
Final note: the DOM extension is probably aware of the <script> tag and knows it isn't allowed to have other tags inside. That explains why inner opening tags are not considered.

best way to inject html using javascript

I'm hoping that this isn't too subjective. I feel there is a definitive answer so here goes.
I want to create this html on the fly using JS (no libraries):
Play
Mute
<div id="progressBarOuter">
<div id="bytesLoaded"></div>
<div id="progressBar"></div>
</div>
<div id="currentTime">0:00</div>
<div id="totalTime">0:00</div>
using javascript. I know I can do this using createElement etc but it seems extremely long winded to do this for each element. Can anyone suggest a way to do this with more brevity.
I do not have access to a library in this project....so no jquery etc.
Keep your markup separate from your code:
You can embed the HTML snippets that you'll be using as hidden templates inside your HTML page and clone them on demand:
<style type="text/css">
#templates { display: none }
</style>
...
<script type="text/javascript">
var node = document.getElementById("tmp_audio").cloneNode(true);
node.id = ""; // Don't forget :)
// modify node contents with DOM manipulation
container.appendChild(node);
</script>
...
<div id="templates">
<div id="tmp_audio">
Play
Mute
<div class="progressBarOuter">
<div class="bytesLoaded"></div>
<div class="progressBar"></div>
</div>
<div class="currentTime">0:00</div>
<div class="totalTime">0:00</div>
</div>
</div>
Update: Note that I've converted the id attributes in the template to class attributes. This is to avoid having multiple elements on your page with the same ids. You probably don't even need the classes. You can access elements with:
node.getElementsByTagName("div")[4].innerHTML =
format(data.currentTime);
Alternatively, you can act on the HTML of the template:
<script type="text/javascript">
var tmp = document.getElementById("tmp_audio").innerHTML;
// modify template HTML with token replacement
container.innerHTML += tmp;
</script>
Shove the entire thing into a JS variable:
var html = 'Play';
html += 'Mute';
html += '<div id="progressBarOuter"><div id="bytesLoaded"></div><div id="progressBar"></div></div>';
html += '<div id="currentTime">0:00</div>';
html += '<div id="totalTime">0:00</div>';
Then:
document.getElementById("parentElement").innerHTML = html;
if you want theN:
document.getElementById("totalTime").innerHTML = "5:00";
You can use
<script type="text/javascript">
function appendHTML() {
var wrapper = document.createElement("div");
wrapper.innerHTML = '\
Play\
Mute\
<div id="progressBarOuter"> \
<div id="bytesLoaded"></div>\
<div id="progressBar"></div>\
</div>\
<div id="currentTime">0:00</div>\
<div id="totalTime">0:00</div>\
';
document.body.appendChild(wrapper);
}
</script>
If you live in 2019 and beyond read here.
With JavaScript es6 you can use string literals to create templates.
create a function that returns a string/template literal
function videoPlayerTemplate(data) {
return `
<h1>${data.header}</h1>
<p>${data.subheader}</p>
Play
Mute
<div id="progressBarOuter">
<div id="bytesLoaded"></div>
<div id="progressBar"></div>
</div>
<time id="currentTime">0:00</time>
<time id="totalTime">0:00</time>
`
}
Create a JSON object containing the data you want to display
var data = {
header: 'My video player',
subheader: 'Version 2 coming soon'
}
add that to whatever element you like
const videoplayer = videoPlayerTemplate(data);
document.getElementById('myRandomElement').insertAdjacentHTML("afterbegin", videoplayer);
You can read more about string literals here
edit: HTML import is now deprecated.
Now with Web Components you can inject HTML using an HTML import.
The syntax looks like this:
<link rel="import" href="component.html" >
This will just load the content of the html file in the href attribute inline in the order it appears. You can any valid html in the loaded file, so you can even load other scripts if you want.
To inject that from JavaScript you could do something of the likes of:
var importTag = document.createElement('link');
importTag.setAttribute('rel', 'import');
importTag.setAttribute('href', 'component.html');
document.body.appendChild(importTag);
At the time I am writing this, Chrome and Opera support HTML imports. You can see an up to date compatibility table here http://caniuse.com/#feat=imports
But don't worry about browsers not supporting it, you can use it in them anyway with the webcomponentsjs polyfill.
For more info about HTML imports check http://webcomponents.org/articles/introduction-to-html-imports/
If you don't need any validation for your syntax (which is what makes createElement() so nice) then you could always default to simply setting the innerHTML property of the element you want to insert your markup inside of.
Personally, I would stick with using createElement(). It is more verbose but there are far less things to worry about that way.
If performance is a concern, stay away from innerHTML. You should create the whole object tree using document.createElement() as many times as needed, including for nested elements.
Finally, append it to the document with one statement, not many statements.
In my informal testing over the years, this will give you the best performance (some browsers may differ).
If HTML is ever declared in a variable, it should be simple and for a very specific purpose. Usually, this is not the right approach.
here's 2 possible cases :
Your HTML is static
Your HTML is dynamic
solution 1
In this case, wrap your HTML in double quotes, make it a string and save it in a variable. then push it inside HTML, here's a demo 👇
HTML
<div id="test"></div>
JavaScript
let selector = document.querySelector("#test");
let demo_1 = "<div id='child'> hello and welcome</div>"
selector.innerHTML = demo_1;
solution 2
In this case, wrap your HTML in back ticks, make it a template literal and save it in a variable. then push it inside HTML,
here, you can use variables to change your content. here's a demo 👇
HTML
<div id="test"></div>
JavaScript
let selector = document.querySelector("#test");
let changes = 'hello and welcome'
let demo_1 = `<div id='child'>${changes}</div>`
selector.innerHTML = demo_1;
You can concatenate raw HTML strings (being careful to escape text and prevent XSS holes), or you can rewrite jQuery (or something similar)
I have a situation where I pass text into a third party library, but if my model isPrivate, I'd like to add an element to the text.
return { id: item.id, text: (item.isPrivate == true) ? "<i class=\"icon-lock\" title=\"Private group.\"></i> " + item.label : item.label };
This creates issues with the way the third party library builds up its markup.
This is never a good idea, but third party libraries are there so that we don't have to write everything ourselves. In a situation like this, you have to rely on passing markup though javascript.
When i find a proper solution to this, I will give you an update

Categories

Resources