I have a button that, when clicked in Chrome or Firefox or Edge, shows the file browser window where I can then select a file and complete the file upload. However, when I click the button in Safari, nothing happens - the file browser window does not appear. I am running on Windows 10 and have a Windows version of Safari installed.
Button:
Upload File
Handler:
$(document).ready(function() {
$("#uploadFile").click(function() {
$("#uploadFileHandle").click();
$('html,body').css('cursor','progress');
var formdata = false;
if (window.FormData) {
formdata = new FormData();
}
$("#uploadFileHandle").on("change", function() {
$('html,body').css('cursor','progress');
var i = 0, len = this.files.length, file;
file = this.files[0];
formdata.append("fileUpload", file)
if (formdata) {
csrftoken();
$.ajax({
url: "/profile/uploadFile",
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function (response) {
if (response.success) {
var res = response.file;
$('html,body').css('cursor','default');
}
}
})
}
});
});
});
Any ideas on how to get the file browser window to appear when the Upload File button is clicked in Safari?
I believe that, for security reasons, some browsers will baulk at programmatic activation of file upload dialogs.
Instead of an a tag, try using the label tag with a for attribute equal to the ID of your file upload input, like this:
<label for="uploadFileHandle">Upload File</label>
When a label has a for attribute, clicking it will automatically also click the element that has a matching ID. This behavior is not blocked by any browsers I know of and will lighten your javascript, too.
body {
padding: 25px;
}
#uploadFile {
transition: all .2s ease-in-out;
text-transform: uppercase;
text-align: center;
padding: 10px 20px;
background-color: #2ecc71;
border: 2px solid #2ecc71;
color: #ffffff;
font-family: sans-serif;
cursor: pointer;
}
#uploadFile:hover {
background-color: #ffffff;
color: #2ecc71;
}
#uploadFileHandle {
visibility: hidden;
}
<label for="uploadFileHandle" id="uploadFile">Upload File</label>
<input type="file" id="uploadFileHandle">
Figured it out thanks to a comment on this post:
How to open a file / browse dialog using javascript?
I apologize for leaving out an important line in my original question:
<input type="file" class="displayNone" id="uploadFileHandle">
Apparently, Safari doesn't like the class="displayNone" on the input element. I got it working by removing the displayNone class and then styling it differently to hide the element. Thanks for the replies everyone!
Related
I am building my first web app using Flask. I'm new to html/ JavaScript/ CSS - please bear with me.
The app does the following: The user uploads an Excel file as follows:
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file" id="file" />
Then they select certain parameters using dropdown lists. When the user clicks "Submit", the data is manipulated using pandas and a new file is exported in Excel.
I managed to add a spinner to the "submit" button using html and CSS. I added an event listener to my JavaScript so the spinner is activated when the button is clicked. At the moment, the spinner runs indefinitely, however I would like the spinner to stop and the button text to revert to "submit" once the operation is finished, i.e. the export is complete. Does anybody know how I can accomplish this?
Here is my html:
<button type="submit" id="submit" class="button">
<span class="button__text">Submit</span>
</button>
Here is my CSS:
<style>
.button {
position: relative;
padding: 8px 16px;
background: #009579;
border: none;
outline: none;
border-radius: 2px;
cursor: pointer;
}
.button:active {
background: #007a63;
}
.button__text {
font: bold 20px "Quicksand", san-serif;
color: #ffffff;
transition: all 0.2s;
}
.button--loading .button__text {
visibility: hidden;
opacity: 0;
}
.button--loading::after {
content: "";
position: absolute;
width: 16px;
height: 16px;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
border: 4px solid transparent;
border-top-color: #ffffff;
border-radius: 50%;
animation: button-loading-spinner 1s ease infinite;
}
/*animate spinner - spin from 0 to 1 turn*/
#keyframes button-loading-spinner {
from {
transform: rotate(0turn);
}
to {
transform: rotate(1turn);
}
</style>
Here is my JavaScript:
<script type="text/javascript">
(() => {
const elem = document.getElementById('submit');
elem.disabled = false;
elem.addEventListener('click', e=> {
elem.classList.add('button--loading');
});
})();
</script>
Basically, your frontend (the HTML/Javascript) needs some way of knowing when "the export is complete". Depending on your definition of "export" and "complete".
At the most rudimentary level, you could have the frontend poll the server every second after the upload starts, and have the server return some kind of status as to what the state of the process is. This could be as simple as:
GET /status/1234
> {"status": "IN_PROGRESS"}
which, when the "export is complete" switches to:
> {"status": "COMPLETE"}
And when the frontend receives the COMPLETE status, it removes the spinner. Notice the 1234. You will need some way of identifying the upload in progress. One way to do this would be to assign it a random id when you first accept the form, so the initial POST /upload returns > {'id': 1234} which the client can then use for the subsequent polling.
If "export is complete" actually only means that the file is finished uploading, you could still use a polling method, but a much slicker way is to use the client (web browser) method of determining how many bytes have been sent (see this SO answer).
I am creating a UI where the user wants to upload her profile picture using <input type="file"> by clicking on its corresponding <label>. I want to show the preview of the image as the background of the same <label>. I tried to use inputNode.files[0] in JavaScript but it does not work.
I am also working on a button X which clears the selected file field values and essentially the background image too but that's the next step of the goal. Some guidance regarding this is also welcome, since I have not thought about this either.
document.getElementById("avatar").onchange = function(e) {
console.log("file changed", e.target.files[0]);
// document.getElementById("preview-img");
document.getElementById("avatar-label").style.backgroundImage = e.target.files[0];
// document.getElementById("avatar-label").style.backgroundImage = 'url("https://picsum.photos/70/70")';
};
#avatar {
display: inline-block;
height: 0;
width: 0;
}
#avatar-label {
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 70px;
width: 70px;
border: solid 1px #333;
/*background: url('https://picsum.photos/70/70');*/
}
#avatar-label:hover {
cursor: pointer;
}
/* styling for unselecting the image */
#avatar-label #unselect-image {
display: none;
position: absolute;
top: 0;
right: 0;
border: none;
outline: none;
background: #fff;
}
<form action="" method="get">
<input accept="image/*" type="file" name="avatar" id="avatar">
<label for="avatar" id="avatar-label">
+
<button type="button" id="unselect-image">X</button>
</label>
<img src="" alt="" id="preview-img">
</form>
Showing in background
Use file reader instead of directly assigning the image object.
You may change your script to show background image as below
document.getElementById('avatar').onchange = function (e){
var file = e.target.files[0];
var reader = new FileReader();
reader.onloadend = function(){
document.getElementById('avatar-label').style.backgroundImage = "url(" + reader.result + ")";
document.getElementById('unselect-image').style.display = "inline";
}
if(file){
reader.readAsDataURL(file);
}
}
Clearing the background
For clearing the background image the following script may help
document.getElementById('unselect-image').onclick = function (){
document.getElementById('avatar-label').style.background = "none";
document.getElementById('unselect-image').style.display = "none";
};
The value of the background-image property in CSS is a string consisting of url(, followed by a URL, followed by ).
It is not a file object.
So you need to take that file object and convert it into a URL. This answer to another question explains how to do that.
Then you need to wrap the result in url( and ) and assign it:
.then( data => {
document.getElementById("avatar-label").style.backgroundImage = `url(${data})`;
})
Here are making thumbnail on uploading image
And to clear selected field is simple.
You assign '' into "src" property of html "img" element and value property of html "input" element.
Replace "label" with "img" if possible.
I have written some code that needs to get triggered by Google Tag Manager. Basically, it injects some HTML elements in a page and displays a popup, after a couple of seconds.
The behaviour as it is intended to work can be seen here: https://codepen.io/jfix/pen/dxdBVK
The Tag configuration is "Custom HTML" and the following code, with a timed trigger:
<style type="text/css">
.centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
div#popup {
display: none;
}
.box {
background: white;
padding: 2em;
}
.bordered {
border-radius: 10px 10px 10px 10px;
box-shadow: 10px 10px 30px 10px rgba(0,0,0,0.75);
}
iframe#popupIframe {
height: 600px;
width: 810px;
border: none;
overflow: hidden;
z-index: 4;
}
#popup #close {
float: right;
font-size: 200%;
font-weight: bold;
cursor: pointer;
}
</style>
<script>
window.addEventListener("load", function() {
try {
var body = document.querySelector("body");
var div = document.createElement("div");
div.innerHTML = '<div class="centered bordered box" id="popup">' +
'<div id="close">×</div>' +
'<iframe id="popupIframe" src="about:blank" data-src="https://survey2018.oecd.org/Survey.aspx?s=0f083fb30cc34e04977ae35d45ce3258"></iframe>' +
'</div>';
var popup = body.insertBefore(div, null);
} catch(e) {
console.log('Error in addEventListener:', e);
}
// display popup and load iframe on demand
function showPopup() {
try {
var iframe = document.getElementById("popupIframe");
var url = iframe.getAttribute("data-src");
iframe.setAttribute("src", url);
var popup = document.getElementById("popup");
popup.style.display = "block";
popup.style.zIndex = "3";
} catch(e) {
console.log('Error in showPopup():', e);
}
}
// hide popup
function dismissPopup() {
try {
var popup = document.getElementById("popup");
popup.style.display = "none";
} catch(e) {
console.log('Error in dismissPopup(): ', e);
}
}
// display popup after 3 seconds
//setTimeout(showPopup, 3000); // GTM has already a waiting period of three seconds ...
showPopup()
// mechanics to dismiss popup when clicking
// close button or anywhere outside it.
popup.addEventListener("click", dismissPopup);
window.addEventListener("click", function(event) {
if (event.target !== popup) {
dismissPopup();
}
});
});
</script>
This works fine as long as I'm in Preview/Debug mode, i.e. the GTM Preview/Debug pane is displayed on the target page.
If I publish the change and test from an Incognito window for example, nothing happens. No error message in the console, nothing.
I've also changed the trigger to see whether the problem was there, to trigger the tag not on a timer, but on window load, no change.
There is an option for support for document.write which I don't use. I've tried with both the option select and deselected. No luck.
Note that I've made sure that no jQuery is used, only Vanilla Javascript, no ES2015 or other advanced features (arrow functions, backtick string templates, nothing fancy).
It turned out that for some reason GTM doesn't like opening and closing elements for the iframe element, like so <iframe src="..."></iframe>. I was able to achieve my goal by using an empty <iframe src="..."/> element. That was all.
I had the same issue.
I had to create a second environment, and publish to that, before it would work outside of preview mode.
So you hit submit -> publish to environment (edit) -> create new environment pointed to your url.
For me I just needed to publish the container by clicking 'submit' (top right). The debug mode apparantly works for non-published containers. You can confirm it is published by clicking on the versions tab.
I am trying using Jquery ui's resizeable and draggable on an appended image. It works in Firefox but in Chrome and Safari the appended image doesn't show up at all.
I am using dropzone.js for my file upload. I call one of their functions to get the image:
Dropzone.prototype.submitRequest = function(xhr, formData, files) {
sendFile(formData);
};
Then I call my own function to put the image into my directory and append the image to the test div:
var sendFile = function(formData){
console.log(formData);
$.ajax({
url : '/MoveFiles.php',
data : formData,
type : 'post',
processData : false,
contentType : false,
success : function(response){
testing();
},
error : function(response){
console.log('error - ' + JSON.stringify(response));
}
});
};
var testing = function(){
$(".test").append($('<img src="' + imgPath + '" class="resize-image" />'));
$( ".test" ).draggable({containment: $("#holder")});
$('.resize-image').resizable({containment: $("#holder")});
}
In chrome and safari when I inspect the element it shows the image was appended to the .test div but it doesn't show on the screen. It's not a css issue, I've removed elements to see if the positioning was throwing it off but it still wouldn't show.
When I take off the resizeable function the image shows up and drags just fine. Could anyone see why it would work in Firefox but not Chrome or Safari?
Based on what I could put together, I made this test:
https://jsfiddle.net/Twisty/yd1ppdp4/
HTML
<div id="holder" class="wrapper">
<div class="test">
</div>
</div>
<button id="execute">
Get Image
</button>
CSS
.wrapper {
padding: 0;
margin: 0;
background: #ddd;
border: 1px solid #ccc;
width: 340px;
height: 240px;
}
.test {
background: #fff;
border: 1px dashed #aaa;
border-radius: 3px;
padding: 15px 3px 3px 3px;
width: 100px;
height: 100px;
position: relative;
}
.resize-image {
position: absolute;
top: 15px;
left: 3px;
width: 100px;
}
jQuery
$(function() {
var testing = function(imgPath) {
$(".test").append($('<img src="' + imgPath + '" class="resize-image" />'));
$(".test").draggable({
containment: $("#holder")
});
$('.resize-image').resizable({
containment: $("#holder")
});
}
$("#execute").click(function() {
testing("https://il7.picdn.net/shutterstock/videos/12588167/thumb/1.jpg");
});
});
This works and allows the Div behind to be dragged, and the image to be resized. I did this in FireFox and moved it over to Chrome & Safari. Both worked fine.
Looking at your new code, it should work as long as imgPath is populated with a URL or URI.
How can one create something like Facebook Badges? What sort of technologies are involved? Can it be done using purely JavaScript? If I have a div with some content inside it, how do I convert that into an image for sharing purposes?
Also see what Medium is doing with their new sharing feature: https://twitter.com/Medium/status/620651949529112576. As you select the text, it generates a graphic for sharing that contains the selected text.
You have placed the following code outside the function onrendered().
var dataURL = canvas.toDataURL();
console.log(dataURL);
Due to which it gets executed before the canvas is even rendered.
Place this code inside onrendered().
Here is the updated fiddle.
Here is the snippet.
function testScrnShot() {
html2canvas(document.getElementById("testing"), {
onrendered: function(canvas) {
document.body.appendChild(canvas);
var dataURL = canvas.toDataURL();
console.log(dataURL);
}
});
}
#testing {
font-family: Arial, sans-serif;
background: #000;
color: #fff;
display: inline-block;
padding: 1em;
border-radius: 2px;
cursor: pointer;
margin: 1em;
}
canvas {
float: right;
margin: 1em;
}
<script src="https://github.com/niklasvh/html2canvas/releases/download/0.5.0-alpha1/html2canvas.js"></script>
<div id="testing" onclick="testScrnShot();"><strong>Hello</strong>, this is a test</div>
Prints the data url in the console now.