export plotly as self-contained html in JavaScript - javascript

In JavaScript, how to export plotly plot as a self-contained html file? Just like in R, we can use saveWidget function. How to do it with JavaScript?
Edit
I tried to use
var a = document.body.appendChild(
document.createElement("a")
);
a.download = "export.html";
a.href = "data:text/html," + document.getElementById("myDiv").innerHTML;
a.click();
But it does work well because it loses all the event, etc.
https://codepen.io/anon/pen/XqZqWX

You can download plotly.js and then call Plotly.newPlot method.
Here are step details.
Get plotly script text
async function getPlotlyScript() {
// fetch
const plotlyRes = await fetch('https://cdn.plot.ly/plotly-latest.js')
// get response as text
return await plotlyRes.text()
}
Get plotly chart data
We need to pass the current chart state in Plot.newPlot method. If the chart data and layout are not controlled by your code, don't worry. The DOM element where plotly builds the chart contains data and layout.
function getChartState () {
const el = document.getElementById('your plotly container id')
return {
data: el.data, // current data
layout: el.layout // current layout
}
}
Compose HTML
Now we have everything to compose our HTML. Don't forget to:
add charset="utf-8" because plotly script has special characters.
escape closing script tag to make browser consider it as a string, not as a tag
async function getHtml() {
const plotlyModuleText = await getPlotlyScript()
const state = getChartState()
return `
<head>
<meta charset="utf-8" />
</head>
<script type="text/javascript">
${plotlyModuleText}
<\/script>
<div id="plotly-output"></div>
<script type="text/javascript">
Plotly.newPlot(
'plotly-output',
${JSON.stringify(state.data)},
${JSON.stringify(state.layout)}
)
<\/script>
`
}
Download HTML document
async function exportToHtml () {
// Create URL
const blob = new Blob([await getHtml()], { type: 'text/html' })
const url = URL.createObjectURL(blob)
// Create downloader
const downloader = document.createElement('a')
downloader.href = url
downloader.download = 'export.html'
// Trigger click
downloader.click()
// Clean up
URL.revokeObjectURL(url)
}
exportToHtml()

Related

Alternate / Equivalent of jQuery.load in pure JavaScript? [duplicate]

I want home.html to load in <div id="content">.
<div id="topBar"> HOME </div>
<div id ="content"> </div>
<script>
function load_home(){
document.getElementById("content").innerHTML='<object type="type/html" data="home.html" ></object>';
}
</script>
This works fine when I use Firefox. When I use Google Chrome, it asks for plug-in. How do I get it working in Google Chrome?
I finally found the answer to my problem. The solution is
function load_home() {
document.getElementById("content").innerHTML='<object type="text/html" data="home.html" ></object>';
}
Fetch API
function load_home (e) {
(e || window.event).preventDefault();
fetch("http://www.yoursite.com/home.html" /*, options */)
.then((response) => response.text())
.then((html) => {
document.getElementById("content").innerHTML = html;
})
.catch((error) => {
console.warn(error);
});
}
XHR API
function load_home (e) {
(e || window.event).preventDefault();
var con = document.getElementById('content')
, xhr = new XMLHttpRequest();
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
con.innerHTML = xhr.responseText;
}
}
xhr.open("GET", "http://www.yoursite.com/home.html", true);
xhr.setRequestHeader('Content-type', 'text/html');
xhr.send();
}
based on your constraints you should use ajax and make sure that your javascript is loaded before the markup that calls the load_home() function
Reference - davidwalsh
MDN - Using Fetch
JSFIDDLE demo
You can use the jQuery load function:
<div id="topBar">
HOME
</div>
<div id ="content">
</div>
<script>
$(document).ready( function() {
$("#load_home").on("click", function() {
$("#content").load("content.html");
});
});
</script>
Sorry. Edited for the on click instead of on load.
Fetching HTML the modern Javascript way
This approach makes use of modern Javascript features like async/await and the fetch API. It downloads HTML as text and then feeds it to the innerHTML of your container element.
/**
* #param {String} url - address for the HTML to fetch
* #return {String} the resulting HTML string fragment
*/
async function fetchHtmlAsText(url) {
return await (await fetch(url)).text();
}
// this is your `load_home() function`
async function loadHome() {
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = await fetchHtmlAsText("home.html");
}
The await (await fetch(url)).text() may seem a bit tricky, but it's easy to explain. It has two asynchronous steps and you could rewrite that function like this:
async function fetchHtmlAsText(url) {
const response = await fetch(url);
return await response.text();
}
See the fetch API documentation for more details.
I saw this and thought it looked quite nice so I ran some tests on it.
It may seem like a clean approach, but in terms of performance it is lagging by 50% compared by the time it took to load a page with jQuery load function or using the vanilla javascript approach of XMLHttpRequest which were roughly similar to each other.
I imagine this is because under the hood it gets the page in the exact same fashion but it also has to deal with constructing a whole new HTMLElement object as well.
In summary I suggest using jQuery. The syntax is about as easy to use as it can be and it has a nicely structured call back for you to use. It is also relatively fast. The vanilla approach may be faster by an unnoticeable few milliseconds, but the syntax is confusing. I would only use this in an environment where I didn't have access to jQuery.
Here is the code I used to test - it is fairly rudimentary but the times came back very consistent across multiple tries so I would say precise to around +- 5ms in each case. Tests were run in Chrome from my own home server:
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
</head>
<body>
<div id="content"></div>
<script>
/**
* Test harness to find out the best method for dynamically loading a
* html page into your app.
*/
var test_times = {};
var test_page = 'testpage.htm';
var content_div = document.getElementById('content');
// TEST 1 = use jQuery to load in testpage.htm and time it.
/*
function test_()
{
var start = new Date().getTime();
$(content_div).load(test_page, function() {
alert(new Date().getTime() - start);
});
}
// 1044
*/
// TEST 2 = use <object> to load in testpage.htm and time it.
/*
function test_()
{
start = new Date().getTime();
content_div.innerHTML = '<object type="text/html" data="' + test_page +
'" onload="alert(new Date().getTime() - start)"></object>'
}
//1579
*/
// TEST 3 = use httpObject to load in testpage.htm and time it.
function test_()
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
{
content_div.innerHTML = xmlHttp.responseText;
alert(new Date().getTime() - start);
}
};
start = new Date().getTime();
xmlHttp.open("GET", test_page, true); // true for asynchronous
xmlHttp.send(null);
// 1039
}
// Main - run tests
test_();
</script>
</body>
</html>
try
async function load_home(){
content.innerHTML = await (await fetch('home.html')).text();
}
async function load_home() {
let url = 'https://kamil-kielczewski.github.io/fractals/mandelbulb.html'
content.innerHTML = await (await fetch(url)).text();
}
<div id="topBar"> HOME </div>
<div id="content"> </div>
When using
$("#content").load("content.html");
Then remember that you can not "debug" in chrome locally, because XMLHttpRequest cannot load -- This does NOT mean that it does not work, it just means that you need to test your code on same domain aka. your server
You can use the jQuery :
$("#topBar").on("click",function(){
$("#content").load("content.html");
});
$("button").click(function() {
$("#target_div").load("requesting_page_url.html");
});
or
document.getElementById("target_div").innerHTML='<object type="text/html" data="requesting_page_url.html"></object>';
<script>
var insertHtml = function (selector, argHtml) {
$(document).ready(function(){
$(selector).load(argHtml);
});
var targetElem = document.querySelector(selector);
targetElem.innerHTML = html;
};
var sliderHtml="snippets/slider.html";//url of slider html
var items="snippets/menuItems.html";
insertHtml("#main",sliderHtml);
insertHtml("#main2",items);
</script>
this one worked for me when I tried to add a snippet of HTML to my main.html.
Please don't forget to add ajax in your code
pass class or id as a selector and the link to the HTML snippet as argHtml
There is this plugin on github that load content into an element. Here is the repo
https://github.com/abdi0987/ViaJS
load html form a remote page ( where we have CORS access )
parse the result-html for a specific portion of the page
insert that part of the page in a div on current-page
//load page via jquery-ajax
$.ajax({
url: "https://stackoverflow.com/questions/17636528/how-do-i-load-an-html-page-in-a-div-using-javascript",
context: document.body
}).done(function(data) {
//the previous request fails beceaus we dont have CORS on this url.... just for illlustration...
//get a list of DOM-Nodes
var dom_nodes = $($.parseHTML(data));
//find the question-header
var content = dom_nodes.find('#question-header');
//create a new div and set the question-header as it's content
var newEl = document.createElement("div");
$(newEl).html(content.html());
//on our page, insert it in div with id 'inserthere'
$("[id$='inserthere']").append(newEl);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>part-result from other page:</p>
<div id="inserthere"></div>
Use this simple code
<div w3-include-HTML="content.html"></div>
<script>w3.includeHTML();</script>
</body>```
This is usually needed when you want to include header.php or whatever page.
In Javascript it's easy especially if you have HTML page and don't want to use php include function but at all you should write php function and add it as Javascript function in script tag.
In this case you should write it without function followed by name Just. Script rage the function word and start the include header.php
i.e convert the php include function to Javascript function in script tag and place all your content in that included file.
I use jquery, I found it easier
$(function() {
$("#navigation").load("navbar.html");
});
in a separate file and then load javascript file on html page
showhide.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function showHide(switchTextDiv, showHideDiv)
{
var std = document.getElementById(switchTextDiv);
var shd = document.getElementById(showHideDiv);
if (shd.style.display == "block")
{
shd.style.display = "none";
std.innerHTML = "<span style=\"display: block; background-color: yellow\">Show</span>";
}
else
{
if (shd.innerHTML.length <= 0)
{
shd.innerHTML = "<object width=\"100%\" height=\"100%\" type=\"text/html\" data=\"showhide_embedded.html\"></object>";
}
shd.style.display = "block";
std.innerHTML = "<span style=\"display: block; background-color: yellow\">Hide</span>";
}
}
</script>
</head>
<body>
<a id="switchTextDiv1" href="javascript:showHide('switchTextDiv1', 'showHideDiv1')">
<span style="display: block; background-color: yellow">Show</span>
</a>
<div id="showHideDiv1" style="display: none; width: 100%; height: 300px"></div>
</body>
</html>
showhide_embedded.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function load()
{
var ts = document.getElementById("theString");
ts.scrollIntoView(true);
}
</script>
</head>
<body onload="load()">
<pre>
some text 1
some text 2
some text 3
some text 4
some text 5
<span id="theString" style="background-color: yellow">some text 6 highlight</span>
some text 7
some text 8
some text 9
</pre>
</body>
</html>
If your html file resides locally then go for iframe instead of the tag. tags do not work cross-browser, and are mostly used for Flash
For ex : <iframe src="home.html" width="100" height="100"/>

Storing data as Streamer javascript

I have a large datasets which i am retrieving via tableau API call. im using async await to call the data and storing this as txt extension.
How i am retrieving the data is by using this script below, script is working as expected and the logic i came out with is
Retrieve data records via api call
Append data into div element
Once data is fully loaded to div, use file streamer to save records as txt file
script used -
<!DOCTYPE html>
<html>
<head>
<title>getData() Basic Example</title>
<script type="text/javascript" src="https://public.tableau.com/javascripts/api/tableau-2.min.js"></script>
<script type="text/javascript">
var viz, sheet, table;
function initViz() {
var containerDiv = document.getElementById("vizContainer"),
url = "http://public.tableau.com/views/RegionalSampleWorkbook/Storms",
options = {
hideTabs: true,
hideToolbar: true,
onFirstInteractive: function () {
document.getElementById('getData').disabled = false; // Enable our button
}
};
viz = new tableau.Viz(containerDiv, url, options);
}
async function savefile(data){
const newHandle = await window.showSaveFilePicker();
const writableStream = await newHandle.createWritable();
await writableStream.write(data)
await writableStream.close();
}
function getUnderlyingData(){
sheet = viz.getWorkbook().getActiveSheet().getWorksheets().get("Storm Map Sheet");
sheet.getUnderlyingDataAsync().then(function(dataTable){
let _tmpdata = ''
for(let i = 0; i < dataTable.getData().length;i++){
for(let a = 0; a < dataTable.getColumns().length;a++){
_tmpdata = dataTable.getData()[i][a].formattedValue;
document.getElementById('storage').innerHTML += _tmpdata
}
}
let whatisthis = document.getElementById('storage').innerHTML
savefile(whatisthis)
});
}
</script>
</head>
<body onload="initViz();">
<div class="page-header">
<button id="getData" onclick="getUnderlyingData()" class="btn" disabled>Get Data</button>
<div id="storage"></div>
</div>
<div id="vizContainer" style="width:600px; height:600px;"></div>
<div id="dataTarget"></div>
</body>
</html>
This is working as expected but what worries me is when i have super large volume of data, the alternative logic i have in mind which i tried to implement is as follow
create streamer inside getunderlyingdata function
append data directly in for loop
New logic i tried, ets say saveFile() does not exist and writableStream are directy implemented in getUnderlyingData, this is script i tried
async function getUnderlyingData(){
// save file to location
const newHandle = await window.showSaveFilePicker();
const writableStream = await newHandle.createWritable();
sheet = viz.getWorkbook().getActiveSheet().getWorksheets().get("Storm Map Sheet");
sheet.getUnderlyingDataAsync().then(async function(dataTable){
let _tmpdata = ''
for(let i = 0; i < dataTable.getData().length;i++){
for(let a = 0; a < dataTable.getColumns().length;a++){
_tmpdata = dataTable.getData()[i][a].formattedValue;
// Write data to stream
await writableStream.write(_tmpdata)
}
}
});
// Close Sream
await writableStream.close();
}
It was not able to capture the data is because the page get reloaded as soon as i tried to save to a location . Is it possible to disable the page reload when a location is selected to save the file ?

Loading local text file to simpleMDE

Lately I have started using simpleMDE in my website, and I ran into a problem loading a local text file to my markdown editor, using a input button.
I was also unable to delete the previous data and change it to "".
I built my website using flask, and I am using flask simpleMDE to load simpleMDE to my website.
my code:
hello.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flask-SimpleMDE example</title>
{{ simplemde.css }}
{{ simplemde.js }}
</head>
<body>
<div>
<input type="file">
<textarea>
Some Markdown Text Here
</textarea>
</div>
<script>
let input = document.querySelector('input')
let textarea = document.querySelector('textarea')
// This event listener has been implemented to identify a
// Change in the input section of the html code
// It will be triggered when a file is chosen.
input.addEventListener('change', () => {
let files = input.files;
if (files.length == 0) return;
/* If any further modifications have to be made on the
Extracted text. The text can be accessed using the
file variable. But since this is const, it is a read
only variable, hence immutable. To make any changes,
changing const to var, here and In the reader.onload
function would be advisible */
const file = files[0];
let reader = new FileReader();
reader.onload = (e) => {
const file = e.target.result;
// This is a regular expression to identify carriage
// Returns and line breaks
const lines = file.split(/\r\n|\n/);
textarea.value = lines.join('\n');
};
reader.onerror = (e) => alert(e.target.error.name);
reader.readAsText(file);
});
var simplemde = new SimpleMDE({
autofocus: true,
autosave: {
enabled: true,
uniqueId: "MyUniqueID",
delay: 1000,
},
});
</script>
</body>
</html>
my app:
from flask import Flask, render_template,request
from flask_simplemde import SimpleMDE
app = Flask(__name__)
app.config['SIMPLEMDE_JS_IIFE'] = True
app.config['SIMPLEMDE_USE_CDN'] = True
SimpleMDE(app)
#app.route('/')
def hello():
return render_template('hello.html')
if __name__ == '__main__':
app.run(debug=True)
When I try running the file without the flask, I obviously can't see the simplemde but seeing the textarea and able to load a text file into it using the input button, and when running the flask app I am able to see the simpleMDE but unable to load a text file into it.
any suggestings?

Issue with API url: cannot seem to pass parameters correctly

I'm new at APIs and am trying for the first time to create a gallery using Flickr API, but I'm really struggling getting it to work properly.
I have created the url correctly, and if I insert the search parameters directly in the url, when I console log I can see the correct list of images. The issue occurs when I'm trying to pass parameters from variables into the url. And the error manifests in two different ways, depending whether I put the url variable before or after the parameters variables.
Case 1:
var url = `https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=XXXXXXXXXXXXXX&text=${searchWord}&per_page=${perPage}&page=${page}&format=json&nojsoncallback=1`;
var searchWord = document.getElementById('input').value;
var perPage = 50;
var page = 1;
In this case it will console log data, but using parameters of its choice.
Case 2:
var searchWord = document.getElementById('input').value;
var perPage = 50;
var page = 1;
var url = `https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=XXXXXXXXXXXXXX&text=${searchWord}&per_page=${perPage}&page=${page}&format=json&nojsoncallback=1`;
In this case it will return the following error message: {stat: "fail", code: 3, message: "Parameterless searches have been disabled. Please use flickr.photos.getRecent instead."}
I seem to not be passing parameters to the url correctly, cause I'm also having an issue when trying to create the gallery itself, where when I'm trying to pass the necessary ids from the images in the API array to the image url I'm creating, these parameters end up being undefined.
This is the code I've written:
function createGallery(photos) {
for (let photo of Object.keys(photos)) {
const imgElem = document.createElement('li');
imgElem.innerHTML = `<img src='https://farm{${photo.farm}}.staticflickr.com/{${photo.server}}/{${photo.id}}_{${photo.secret}}_[mstzb].jpg'></img>`;
imgElem.setAttribute('img-id', photo.id);
gallery.append(imgElem);
}
}
Just for an overview, here is the last bit of my JS:
//Fetch API
async function start() {
const response = await fetch (url)
const data = await response.json()
console.log(data)
createGallery(data)
}
//Call functions
search()
And here is my HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<input type="text" id="input">
<button id="search">Go</button>
<ul id="gallery"></ul>
</main>
<script src="script.js"></script>
</body>
</html>
Anyone can explain what I'm doing wrong?? Thanks so much in advance :)
EDIT: I tried appending the variables using pluses instead of using template literals, like so:
var url = 'https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=XXXXXXXXXXXXXX&text='+searchWord+'&per_page='+perPage+'&page='+page+'&format=json&nojsoncallback=1';
But both ways give exactly the same result.
You are basically appending the parameters incorrectly.
You have the following:
var perPage = 50;
var page = 1;
var url = `https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=XXXXXXXXXXXXXX&text=${searchWord}&per_page=${perPage}&page=${page}&format=json&nojsoncallback=1`;
When you define that url it should be similar to this ..
var url = `https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=XXXXXXXXXXXXXX&text=' + searchWord + '&per_page=' + perPage + '&page=' + page + '&format=json&nojsoncallback=1'
Please note the difference there, we are actually just building up the url var by appending the JS variables to the String. There is no ${} access to the declared page and perPage JS vars...

I want the html obtained from the text area to run in the browser's new tabb.Is that possible?

Can I run html in a variable in a new tab?
example:
I want the html obtained from the text area to run in the browser's new tab.
Is that possible?
$('body').on('click', '.excute_html_button', function (e) {
const id = this.id;
const html = $("#title-area-"+id).val()
// is it possible?
// new tab open and execute html
});
Either vanilla JS or jQuery.
example html vue code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Getting Started</title>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js'
}
})
</script>
</body>
</html>
You told me the answer, but I still have a problem ^^;
vue code
There is a problem that the html is output as it is.
{{text}}
Maybe it's because the code ran inside the body
Is there a way to solve this problem?
You can open a new window or tab using window.open, save a reference to the opened window, then assign to the innerHTML of its document:
$('body').on('click', '.excute_html_button', function (e) {
const id = this.id;
const html = $("#title-area-"+id).val()
const w = window.open();
w.document.body.innerHTML = html;
});
The user may have popup blockers, in which case you'd want to check if the window exists first, and display a message to the user if it couldn't be opened:
$('body').on('click', '.excute_html_button', function (e) {
const id = this.id;
const html = $("#title-area-"+id).val()
const w = window.open();
if (!w) {
console.error("Window couldn't be opened, please permit popups from this site for full functionality");
// or put the above message into an HTML element
return;
}
w.document.body.innerHTML = html;
});
This may open a new tab or it may open a new window, IIRC, it depends on the user's browser settings, and isn't something Javascript can choose between.
To include Javascript and Vue, you won't be able to execute scripts by changing the innerHTML. Instead, you have to insert the <script> tag using document.createElement('script') - first insert Vue, wait for it to load, then insert the new script tag with other Javascript that invokes it:
const w = window.open();
const html = `<div id="app">
<button #click="disabled = (disabled + 1) % 2">Toggle Enable</button>
<input type="text" :disabled="disabled == 1">
<pre>{{ $data }}</pre>
</div>`;
w.document.body.innerHTML = html;
const script = w.document.body.appendChild(w.document.createElement('script'));
script.src = "https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js";
script.onload = () => {
w.document.body.appendChild(w.document.createElement('script')).textContent = `
var app = new Vue({
el: '#app',
data: {
disabled: 0,
},
});`;
};
Example:
https://jsfiddle.net/dvumwxfo/

Categories