im trying to download a file that constructs itself based on the value it recives. This is my code
<html>
<head>
<script>
var myList=[];
window.onmessage = function(event){
if (event.data) {
myList = event.data;
if (myList.length>0) {
buildHtmlTable();
}
}
else {
myList = [];
}
};
function buildHtmlTable() {
var columns = addAllColumnHeaders(myList);
for (var i = 0 ; i < myList.length ; i++) {
var row$ = $('<tr/>');
for (var colIndex = 0 ; colIndex < columns.length ; colIndex++) {
var cellValue = myList[i][columns[colIndex]];
if (cellValue == null) { cellValue = ""; }
row$.append($('<td/>').html(cellValue));
}
$("#excelDataTable").append(row$);
}
return exportF(); // Make Excel file download now
}
function addAllColumnHeaders(myList)
{
var columnSet = [];
var headerTr$ = $('<tr/>');
for (var i = 0 ; i < myList.length ; i++) {
var rowHash =`enter code here` myList[i];
for (var key in rowHash) {
if ($.inArray(key, columnSet) == -1){
columnSet.push(key);
headerTr$.append($('<th/>').html(key));
}
}
}
$("#excelDataTable").append(headerTr$);
return columnSet;
}
function exportF() {
var table = document.getElementById("excelDataTable");
var html = table.outerHTML;
var url = 'data:application/vnd.ms-excel,' + escape(html);
var link = document.getElementById("downloadLink");
link.setAttribute("href", url);
link.setAttribute("download", "export.xls"); // Choose the file name here
link.click(); // Download your excel file
return false;
}
</script>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body onLoad="">
<table id="excelDataTable" border="1">
</table>
<a style="display: none" id="downloadLink"></a>
</body>
</html>
The code itself works, but the error i get is "Download is disallowed. The frame initiating or instantiating the download is sandboxed, but the flag ‘allow-downloads’ is not set. See https://www.chromestatus.com/feature/5706745674465280 for more details."
What can i do to work around this? It feels like ive tried everything i can get my hands on but nothing seems to work for it to download
As the warning message says, you can't initialize downloads from a sandboxed iframe if it doesn't have the allow-downloads permission.
All solutions will imply having access to the page where the iframe is displayed (hereafter "the embedder").
The easiest and recommended way,
is to ask the embedder to add this permission when they define their iframe:
<iframe src="yourpage.html" sandbox="allow-scripts allow-downloads"></iframe>
An other way would be to ask them to not sandbox that iframe at all,
<iframe src="yourpage.html"></iframe>
but I guess that if they did, it's because they don't trust your page enough.
Finally a more complex way would be to pass the generated file back to the parent window.
For this you'd need to define a new API with your clients.
You could obviously just emit a global message event back to them, but I guess the cleanest is to make them pass a MessageChannel's MessagePort along with the myList data, so they can wait for the response there easily and be sure they'll only catch the response and no other unrelated message.
So in the embedder page they'd do
frame.onload = (evt) => {
const channel = new MessageChannel();
// handle the response from the iframe
channel.port2.onmessage = (evt) => {
const file = evt.data;
saveAs( file, "file.html" ); // the embedder is reponsible to initialize the download
};
frame.contentWindow.postMessage( embedders_data, [ channel.port1 ] );
};
And in your page you'd do
window.onmessage = (evt) => {
const myList = evt.data;
// get the MessageChannel's port out of the transfer-list
const port = evt.ports[ 0 ];
// buildHtmlTable has to return the final file, not to make it download
const file = buildHtmlTable( myList );
if( port ) {
port.postMessage( file ); // send back to embedder
}
};
See it as a live plnkr.
Ps: note that your files are not xlsx files but HTML markup.
The correct answer
Under normal circumstances Kaiido's answer is indeed the correct solution to your problem. They will NOT work in your case though.
The answer that will work on WixSince you are using Wix there is no way for you to directly edit the Sandbox attribute of the iframe element. This is just how Wix does things. You can, however, use custom code (only applies to premium websites) to get the class name of the iframe and programatically use javascript to set the new attribute to the existing iframe.
You must use the web inspector to find out the class name (iframes in Wix do not have ids) then add "allow-downloads" to the sandbox attribute. You might then need to reload the iframe using js as well. Go to your website's settings -> Custom Code -> Create custom code at the end of the body tag
If you do not have a premium website then you unfortunately cannot do this. This is due to Wix's own limitations as a platform. If this is an absolute "must" for you project, I recommend you to not use Wix since they limit your freedom as a developer when it comes to working with
technologies that were not made by them. Not to mention that they lock features such as custom elements behind a pay wall. So we can't even test our ideas before committing to a hosting plan. For anyone reading this in the future, take this into consideration and look into other platforms.
Thanks for answers, i didnt find a sollution with the recomended answers. What i did is that i made a completely new page, instead of initializing a html iframe i redirected the current window to the new page i created. The new page took a variable from "www.page.com/?page={value} and downloaded what i needed from there instead. Its messy but it works so if anyone else has this problem i recomend this if you are using wix.
Related
i want the new html file to be created in the same folder in which HTML is present. please help me. am searching a lot , no luck
<script>
function makeDocument() {
var doc = document.implementation.createHTMLDocument("newdoc");
var p = doc.createElement("p");
p.innerHTML = "This is a new paragraph.";
try {
doc.body.appendChild(p);
} catch(e) {
console.log(e);
}
var opened = window.open("");
opened.document.write(doc);
}
</script>
Use a back-end server. Because the HTML page and the scripts are executed on the client-side. You can't really create a file on the client-side while the page is loaded in a browser.
The other way around, you won't want the client to create arbitrary files on the server as well. It poses a great security risk and might lead to possible remote code execution (RCE).
You do not have a closing brace on your function
<script>
function makeDocument() {
var doc = document.implementation.createHTMLDocument("newdoc");
var p = doc.createElement("p");
p.innerHTML = "This is a new paragraph.";
try {
doc.body.appendChild(p);
} catch(e) {
console.log(e);
}
var opened = window.open("");
opened.document.write(doc);
}
</script>
As for the wording of your question, if you are asking to create a new file that gets saved, then Aviv Lo's answer is what you need
This a basic question (Posting this again, as it was not re-opened after I updated the question). But I couldn't find any duplicates on SO.
This is a script I intend to use in my project on different pages. The purpose is to override the default ID shown in a span element to the order number from the URL parameter session_order. This doesn't affect anything and only enhances the UX for my project.
scripts.js (loaded in the header):
function get_url_parameter(url, parameter) {
var url = new URL(url);
return url.searchParams.get(parameter);
}
And in my HTML template, I call the function this way,
<div onload="this.innerHTML = get_url_parameter(window.location.href, 'session_order');">24</div>
Also tried this,
<div><script type="text/javascript">document.write(get_url_parameter(window.location.href, 'session_order'));</script></div>
When the page is rendered, nothing changes. No errors or warnings in the console either for the first case.
For the second case, console logged an error Uncaught ReferenceError: get_url_parameter is not defined, although script.js loads before the div element (without any errors).
Normally, I'd do this on the server-side with Flask, but I am trying out JavaScript (I am new to JavaScript) as it's merely a UX enhancement.
What am I missing?
Try this:
// This is commented because it can't be tested inside the stackoverflow editor
//const url = window.location.href;
const url = 'https://example.com?session_order=13';
function get_url_parameter(url, parameter) {
const u = new URL(url);
return u.searchParams.get(parameter);
}
window.onload = function() {
const el = document.getElementById('sessionOrder');
const val = get_url_parameter(url, 'session_order');
if (val) {
el.innerHTML = val;
}
}
<span id="sessionOrder">24</span>
Define the function you need for getting the URL param and then on the window load event, get the URL parameter and update the element.
Here you go. Try to stay away from inline scripts using document.write.
function get_url_parameter(url, parameter) {
var url = new URL(url);
return url.searchParams.get(parameter);
}
window.addEventListener('load', function() {
const url = 'https://yourpagesdomain.name/?session_order=hello'; //window.location.href;
const sessionOrder = get_url_parameter(url, 'session_order');
document.getElementById('sessionOrder').innerText = sessionOrder;
});
<div id="sessionOrder"></div>
The order of your markup and script matters...
<div></div>
<script>
function get_url_parameter(url, parameter) {
var url = new URL(url);
return url.searchParams.get(parameter);
}
</script>
<script>
document.querySelector('div').innerHTML = get_url_parameter('https://example.com?session_order=2', 'session_order');
</script>
I am trying to use the History API to allow me to use the back and forward buttons when loading content dynamically. Here is the code I am using, and an example of the state object I am using too.
How I am using the state object and pushstate()
var stateObj = {Content : homeSection.innerHTML, "Product" : detail.Name, Title : title.innerHTML, Section:"dynamicArticle"};
window.history.pushState(stateObj, "", detailName);
window.addEventListener('popstate', function(event) {
updateContent(event.state);
});
Function used:
function updateContent(stateObject) {
if (stateObject){
homeSection = document.getElementById(stateObject.Section);
homeSection.innerHTML = stateObject.Content;
title.innerHTML = stateObject.Title;
var items = document.querySelectorAll(".homeItem");
if(items){
for(i=0; i<items.length; i++){
items[i].addEventListener("click", selectedProduct);
}
}
checkoutButton = document.getElementById('checkoutButton');
if(checkoutButton){
checkoutButton.addEventListener('click', function(){
displayCheckout();
});
}
basketButton = document.getElementById("basketButton");
quantityInput = document.getElementById("productQuantity");
if(basketButton){
basketButton.addEventListener('click', clicked);
basketButton.addEventListener('click', updateBasketNumber);
quantityInput.value = "1";
}
searchSort = document.getElementById("sort");
if(searchSort){
var items = document.querySelectorAll(".searchResult");
for(i=0; i<items.length; i++){
items[i].addEventListener("click", selectedProduct);
}
searchSort.addEventListener("change", function(){
sort = searchSort.value;
searchItem(e, sort);
});
}
}
else{
return;
}
}
What I am struggling with is that if I navigate to one of the pages using the pushState() and I try to reload the page, as you would expect the page cannot be found.
I am asking if there is a way to allow a reload or for someone to navigate to the URL without it giving an error, and giving the correct content
Just like #jon-koops pointed in the comment you need to configure your server to redirect requests to the same page where all you links branch.
If you are using apache 2.2.16+ it get's as simple as:
FallbackResource /index.html
This will rewrite all URL’s to a single entry point that is index.html page.
Other solutions depend on the server you are running.
If a specific <div id> is removed from the HTML document, I want it the user to be redirected to a new web page using JavaScript.
For example:
<div id="credits"></div>
If someone removes it then users will be automatically redirected to my website.
This is to protect copyrights.
The best you can probably do is to just poll for the existence of that div, and redirect if it's not there. Also, be sure to check that it's actually visible, per Philip's comment.
But of course any user can just turn this script off, so I'm really not sure it's even worth the effort.
setInterval(function(){
if (!$('#credits:visible').length) window.location.href = 'wherever.com';
}, 3000);
You want a MutationObserver, but it's not widely supported: http://jsfiddle.net/xNAXd/.
var elem = document.getElementById("credits");
new MutationObserver(function(mutations) {
for(var i = 0; i < mutations.length; i++) {
var index = Array.prototype.indexOf.call(mutations[i].removedNodes, elem);
if(~index) {
alert("Deleted!");
break;
}
}
}).observe(elem.parentNode, {
childList: true
});
I'm working on my first Chrome Extension. After learning some interesting notions about jquery i've moved to raw javascript code thanks to "Rob W".
Actually the extension do an XMLHttpRequest to a remote page with some parameters and, after manipulating the result, render an html list into the popup window.
Now everything is up and running so i'm moving to add some option.
The first one was "how many elements you want to load" to set a limit to the element of the list.
I'm using fancy-setting to manage my options and here's the problem.
The extension act like there's a "cache" about the local storage settings.
If i do not set anything and perform a clean installation of the extension, the default number of element is loaded correctly.
If i change the value. I need to reload the extension to see the change.
Only if a remove the setting i see the extension work as intended immediately.
Now, i'm going a little more into specific information.
This is the popup.js script:
chrome.extension.sendRequest({action: 'gpmeGetOptions'}, function(theOptions) {
//Load the limit for topic shown
console.log('NGI-LH -> Received NGI "max_topic_shown" setting ('+theOptions.max_topic_shown+')');
//Initializing the async connection
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://gaming.ngi.it/subscription.php?do=viewsubscription&pp='+theOptions.max_topic_shown+'&folderid=all&sort=lastpost&order=desc');
xhr.onload = function() {
var html = "<ul>";
var doc = xhr.response;
var TDs = doc.querySelectorAll('td[id*="td_threadtitle_"]');
[].forEach.call(TDs, function(td) {
//Removes useless elements from the source
var tag = td.querySelector('img[src="images/misc/tag.png"]'); (tag != null) ? tag.parentNode.removeChild(tag) : false;
var div_small_font = td.querySelector('div[class="smallfont"]'); (small_font != null ) ? small_font.parentNode.removeChild(small_font) : false;
var span_small_font = td.querySelector('span[class="smallfont"]'); (small_font != null ) ? small_font.parentNode.removeChild(small_font) : false;
var span = td.querySelector('span'); (span != null ) ? span.parentNode.removeChild(span) : false;
//Change the look of some elements
var firstnew = td.querySelector('img[src="images/buttons/firstnew.gif"]'); (firstnew != null ) ? firstnew.src = "/img/icons/comment.gif" : false;
var boldtext = td.querySelector('a[style="font-weight:bold"]'); (boldtext != null ) ? boldtext.style.fontWeight = "normal" : false;
//Modify the lenght of the strings
var lenght_str = td.querySelector('a[id^="thread_title_"]');
if (lenght_str.textContent.length > 40) {
lenght_str.textContent = lenght_str.textContent.substring(0, 40);
lenght_str.innerHTML += "<span style='font-size: 6pt'> [...]</span>";
}
//Removes "Poll:" and Tabulation from the strings
td.querySelector('div').innerHTML = td.querySelector('div').innerHTML.replace(/(Poll)+(:)/g, '');
//Modify the URL from relative to absolute and add the target="_newtab" for the ICON
(td.querySelector('a[id^="thread_title"]') != null) ? td.querySelector('a[id^="thread_title"]').href += "&goto=newpost" : false;
(td.querySelector('a[id^="thread_goto"]') != null) ? td.querySelector('a[id^="thread_goto"]').href += "&goto=newpost": false;
(td.querySelector('a[id^="thread_title"]') != null) ? td.querySelector('a[id^="thread_title"]').target = "_newtab": false;
(td.querySelector('a[id^="thread_goto"]') != null) ? td.querySelector('a[id^="thread_goto"]').target = "_newtab": false;
//Store the td into the main 'html' variable
html += "<li>"+td.innerHTML+"</li>";
// console.log(td);
});
html += "</ul>";
//Send the html variable to the popup window
document.getElementById("content").innerHTML = html.toString();
};
xhr.responseType = 'document'; // Chrome 18+
xhr.send();
});
Following the background.js (the html just load /fancy-settings/source/lib/store.js and this script as Fancy-Setting How-To explains)
//Initialization fancy-settings
var settings = new Store("settings", {
"old_logo": false,
"max_topic_shown": "10"
});
//Load settings
var settings = settings.toObject();
//Listener who send back the settings
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.action == 'gpmeGetOptions') {
sendResponse(settings);
}
});
The console.log show the value as it has been cached, as i said.
If i set the value to "20", It remain default until i reload the extension.
If i change it to 30, it remain at 20 until i reload the extension.
If something more is needed, just ask. I'll edit the question.
The problem appears to be a conceptual misunderstanding. The background.js script in a Chrome Extension is loaded once and continues to run until either the extension or the Chrome Browser is restarted.
This means in your current code the settings variable value is loaded only when the extension first starts. In order to access values that have been updated since the extension is loaded the settings variable value in background.js must be reloaded.
There are a number of ways to accomplish this. The simplest is to move the settings related code into the chrome.extension.onRequest.addListener callback function in background.js. This is also the most inefficient solution, as settings are reloaded every request whether they have actually been updated or not.
A better solution would be to reload the settings value in background.js only when the values are updated in the options page. This uses the persistence, or caching, of the settings variable to your advantage. You'll have to check the documentation for implementation details, but the idea would be to send a message from the options page to the background.js page, telling it to update settings after the new settings have been stored.
As an unrelated aside, the var keyword in the line var settings = settings.toObject(); is not needed. There is no need to redeclare the variable, it is already declared above.