How to create a thumbnail from image with close button - javascript

Here is my code:
https://codepen.io/manuchadha/pen/PBKYBJ
I have created a form. I want to be able to upload an image using the file upload input. When an image is selected, I want to show a thumbnail of the image just below the file selector box and also show a close (x) sign on the top-right corner of the image which could be used to delete the image. But I am unable to create it. What am I doing wrong?
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<base href="">
<title>Example</title>
<!--meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'"-->
<link rel="stylesheet" media="screen" href="fiddle.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.1/css/bootstrap-select.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="fiddle.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.1/js/bootstrap-select.min.js"></script>
</head>
<body>
<div id="form-div" class="body__div--background"> <!-- takes the complete width/height of content section -->
<!-- The novalidate attribute in the <form> element prevents the browser from attempting native HTML validations.
validation will be done using Angular's Validators which come with FormGroups and FormControls-->
<form id="new-question-form" class="practice-question-form" [formGroup]="practiceQuestionForm" (ngSubmit)="addPracticeQuestion()" novalidate>
<!-- label and small in same line. select in a new line, thus enclosed select in a div-->
<div class="form-group">
<div class="form-group">
<label for="file-upload" class="control-label required">Upload files</label>
<div class="custom-file" id="file-upload" lang="es">
<input type="file" class="custom-file-input" id="question-file-upload" onchange="handleFileSelect()">
<label class="custom-file-label" for="question-file-upload">
Select file...
</label>
</div>
</div>
<button type="submit" id="submit-practice-question-button" class="content-div__button--blue"> Submit! </button>
</form>
<div id="imageContainer">
</div>
</div>
</div>
</body>
CSS
body{
margin:0px;
}
.body__div--background {
background: linear-gradient(45deg,#33b1f8 37%,#6e90f6 100%); /*syntax linear-gradient(direction, color1 limit, color2 limit)*/
color:#555555;
font-family: Helvetica;
line-height:1.5;
font-size: 11px;
letter-spacing: 0.25px;
}
#submit-practice-question-button{
display:block;
}
#imageContainer{
display:inline-block;
border: 1px solid black;
}
.close {
top:0;
right:80; /*match the width of the image*/
position: relative;
opacity: 0.3;
}
.close:hover {
opacity: 1;
}
.close:before, .close:after {
position: relative;
left: 15px;
content: ' ';
height: 33px;
width: 2px;
background-color: #333;
}
.close:before {
transform: rotate(45deg);
}
.close:after {
transform: rotate(-45deg);
}
JS
/*handler for file upload*/
function handleFileSelect(){
console.log("got file upload event:");
/*
FileList object is the object returned as a result of a user selecting files using the <input> element,
from a drag and drop operation's DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement.
*/
var files = document.getElementById('question-file-upload').files;//event.target.files;
console.log("files selected:"+files+", total selected: "+files.length);
for(var i=0;i<files.length;i++)
{
console.log("files name:"+files[i].name)
console.log("files object:"+files[i])
}
//working with only 1 file at the moment
var file = files[0];
if (files && file) {
/*
The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer,
using File or Blob objects to specify the file or data to read.
*/
var reader = new FileReader();
/*bind onload event of FileReader to _handleReaderLoaded
onload is a handler for the load event. This event is triggered by FileReader each time the reading operation is successfully completed.
*/
reader.onload =this._handleReaderLoaded.bind(this);
reader.readAsBinaryString(file);
}
}
function _handleReaderLoaded(readerEvt) {
var binaryString = readerEvt.target.result;
var base64textString= btoa(binaryString);
console.log(btoa(binaryString));
var src = "data:image/png;base64,";
src += base64textString;
var newImage = document.createElement('img');
newImage.src = src;
newImage.width = newImage.height = "80";
var closeButtonLink = document.createElement('a');
closeButtonLink.setAttribute('href',"#");
closeButtonLink.classList.add("close");
document.querySelector('#imageContainer').appendChild = newImage;
document.querySelector('#imageContainer').appendChild = closeButtonLink;
}

appendChild is a method, not a property.
For example instead of node.appendChild = newImage; it should be node.appendChild(newImage);
Also you needed to add the "X" in your anchor tag. I included that in the example below.
One more thing I did a small performance upgrade too where you save the reference to the query in a variable so you don't need to query the DOM twice.
var closeButtonLink = document.createElement('a');
closeButtonLink.textContent = "X";
closeButtonLink.setAttribute('href', "#");
closeButtonLink.classList.add("close");
// use a var here to only query once for imageContainer
var imgc = document.querySelector('#imageContainer');
imgc.appendChild(newImage);
imgc.appendChild(closeButtonLink);

Related

Want web page (js) file upload without form submission with servlet

I am attempting to write a web page that allows an upload of one or more files to a servlet without making a form submission.
I'm willing to use jQuery and/or Ajax; I do not want to use other third-party libraries.
I have a servlet that works WITH a form submission; I can make alterations to that if necessary to make it work without a form submission:
package ajaxdemo;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
/* The Java file upload Servlet example */
#WebServlet(name = "FileUploadServlet", urlPatterns = { "/fileuploadservlet" })
#MultipartConfig
(
fileSizeThreshold = 1024 * 1024 * 1, // 1 MB
maxFileSize = 1024 * 1024 * 10, // 10 MB
maxRequestSize = 1024 * 1024 * 100 // 100 MB
)
public class FileUploadServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Request-Method", "POST");
/* Receive file uploaded to the Servlet from the HTML5 form */
System.out.println("FileUploadServlet.doPost() invoked");
Part filePart = request.getPart("file");
String fileName = filePart.getSubmittedFileName();
for (Part part : request.getParts())
{
part.write("C:\\tmp\\" + fileName);
}
response.getWriter().print("The file uploaded sucessfully.");
response.getWriter().print("Filename: " + fileName + " saved in //tmp");
}
}
This works with the following input form:
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>File Upload Form</title>
</head>
<body>
<h3>File Upload:</h3>
Select a file to upload: <br />
<form action = "UploadFile.jsp" method = "post"
enctype = "multipart/form-data">
<input type = "file" name = "file" size = "50" />
<br />
<input type = "submit" value = "Upload File" />
</form>
</body>
</html>
In trying to make it work without the form submission, I have the following page:
<html>
<head>
<!-- after https://www.w3schools.com/howto/howto_css_modals.asp -->
<style>
body{font-family: Arial, Helvetica, sans-serif; }
/* file Upload dialog (from w3schools howto_css_modals) */
.fileUploadDialogClass
{
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* "Modal Content" (from w3schools howto_css_modals) */
.fileUploadDialogClassContentClass
{
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
/* "The Close Button" (from w3schools howto_css_modals) */
.fileUploadDialogCloseButtonClass
{
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
/* (from w3schools howto_css_modals) */
.fileUploadDialogCloseButtonClass:hover,
.fileUploadDialogCloseButtonClass:focus
{
color: #000;
text-decoration: none;
cursor: pointer;
}
#upperLeft { background-color: lightgreen; border: 3px solid; }
#licenseeCityState {background-color: lightblue; }
#buttonDiv button { width: 100%; }
#mainTable { width: 100%; }
#mainTable { border: 1px solid; }
</style>
</head>
<body>
<script src="http://code.jquery.com/jquery-3.6.0.js"></script>
<!-- file upload popup dialog -->
<div id="fileUploadDialog" class="fileUploadDialogClass">
<div class="fileUploadDialogClassContentClass">
<span class="fileUploadDialogCloseButtonClass">×</span> <!-- 'times' is an 'x' for urh corner -->
<P>Select a file, then upload it to be read</P>
<br><input type="file" id="fileUploadChooserButton">
<br><button id="fileUploadButton">Upload</button>
</div>
</div>
<table>
<tr>
<td>
<div id='buttonDiv'>
<table id='buttonTable'>
<tr><td><button id='openButton'>Open File</button></td></tr>
<tr><td><button id='closeButton'>Close</button></td></tr>
</table>
</div>
</td>
<td style="vertical-align: top">
<div id='lowerRight'>
<table id='mainTable'>
<tr><td><div id="idString">xxx</div></td></tr>
</table>
</div>
</td>
</tr>
</table>
<script>
document.getElementById("idString").innerText = "xyz2"; // used to keep track of which version is displayed.
var fileUploadDialog = document.getElementById("fileUploadDialog");
var fileUploadDialogDisplayButton = document.getElementById("openButton");
var fileUploadDialogCloseButton = document.getElementsByClassName("fileUploadDialogCloseButtonClass")[0];
var fileUploadButton = document.getElementById("fileUploadButton");
//fileUploadButton.onclick = uploadFile();
fileUploadDialogDisplayButton.onclick = function() { fileUploadDialog.style.display = "block"; }
fileUploadDialogCloseButton.onclick = function() { fileUploadDialog.style.display = "none"; }
//async function uploadFile()
fileUploadButton.onclick = function()
{
console.log("uploadFile() invoked");
let formData = new FormData();
var fileUploadChooserButton = document.getElementById("fileUploadChooserButton");
var files = fileUploadChooserButton.files;
formData.append(files.name, files[0], files[0].name || "no filename")
;
console.log("about to await fetch");
// await fetch('http://localhost:8080/AjaxWithJSP/fileuploadservlet', { method: "POST", body: formData });
const xmlRequest = new XMLHttpRequest();
xmlRequest.onload = () =>
{
alert(xmlRequest.status + " reported as onload status");
};
//http://localhost:8080/AjaxWithJSP/LittleTable.html
xmlRequest.open('POST', 'http://localhost:8080/AjaxWithJSP/fileuploadservlet', true);
xmlRequest.setRequestHeader("Content-type", "multipart/form-data");
xmlRequest.send(formData);
}
window.onclick = function(event) { if(event.target == fileUploadDialog) { fileUploadDialog.style.display = "none"; } }
</script>
</body>
</html>
This produces an error message from the server (in the eclipse console) saying that no multipart boundary is found.
If I comment out the JavaScript line setting the request header, the error message is that filePart is null, so getSubmittedFileName() can't be called on it.
I found another explanation of doing it that involved await fetch(...) instead of xmlRequest.send(...); I have it commented out above. I couldn't make it work either.
Eventually, I want to allow the user to upload multiple files, and return a JSON structure with which I'll display a table. But I haven't figured out how to get the first file uploaded yet.
xmlRequest.setRequestHeader("Content-type", "multipart/form-data");
The multipart/form-data has a mandatory parameter describing the boundary that appears between each of the multiple parts.
Under normal circumstances, xhr or fetch will generate the whole Content-Type header, including the boundary parameter from the FormData object.
Here, you've overridden the Content-type and set it to multipart/form-data without a boundary.
Just don't do that.

Importing a function into a new .html file

I am trying to import a function of a simple button click opening up a box from one .html file to another and for some reason it isn't working. It works perfectly fine when all the css js and html is in its own file, but when they are apart I cant get the button to open when clicked.
regions.html file
<style> #btn_region {
position: absolute; /* create a positioning context for the list items */
width: 23%;
left: 51vw;
top: 130vw;
font-size: 14px;
height: 9vw;
z-index: 1000;
}
#box_region {
position: absolute;
width: 90%;
height: 27%;
background-color: #f1f1f1;
left: 0px;
top: 138vw;
}
.regions {
width:80%;
height:60.15%;
}}
</style>
<button id='btn_region'>Region</button>
<div id='box_region' style='display:none;'>
<div id='div_region'>
<a id='neus' href='$(url)'>
<h1><span class='color-change' >Northeast</span></h1>
<img class='regions'
src='#' width='300'
height='225'>
</a>
</div>
<div id='div_region'>
<a id='usa' href='$(url)'>
<h1><span class='color-change' >USA</span></h1>
<img class='regions'
src='#' width='300'
height='225'>
</a>
</div>
</div>
script.js file
const regions = document.querySelector('.regions')
fetch('regions.html')
.then(res=>res.text())
.then(data=>{
regions.innerHTML=data
const parser = new DOMParser()
const doc = parser.parseFromString(data, 'text/html')
eval(doc.querySelector('script').textContent)
})
let mainDirectory = '#'
let neus_href = document.getElementById('neus');
neus_href.href = mainDirectory+'neus.html';
let usa_href = document.getElementById('usa');
usa_href.href = mainDirectory+'usa.html';
const btn_region = document.getElementById('btn_region');
const box_region = document.getElementById('box_region');
btn_region.addEventListener('click', function() {
if (box_region.style.display === 'block') {
box_region.style.display = 'none';
} else {
box_region.style.display = 'block';
}
});
document.addEventListener('click', function(event) {
if (event.target !== btn_region && event.target !== box_region) {
box_region.style.display = 'none';
}
});
file I'm importing into
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div class="regions">
</div>
<script src="script.js"></script>
</body>
</html>
Spotted a syntax error in "regions.html". I think there's an extra curly bracket in your last style rule.
.regions {
width:80%;
height:60.15%;
}} <-------
Also, I'm getting a console error: neus_href is null
I have a feeling this is because you're trying to select #neus as a generated DOM element, with:
let neus_href = document.getElementById('neus');
I imagine this would work fine if all the HTML code is together on one page, but when the HTML is split into separate files you should probably grab it from your fetch data, which it looks like you're storing in the 'doc' variable. Should probably try something like:
let neus_href = doc.querySelector('#neus');
Hope this helps.

How to create element within for loop in Javascript

I have one text box and one button in my code. I want to let the user to enter a text and click the button. If so a card will be created with the user entered text, and the background color of the card will be set using a json file.
But in my code if the user clicks the button for the second time, previously created card disappears and a new card is being created leaving the space of previously created card. But I want all the cards to be aligned one below one.
I think this can be done using a loop function by setting different ids to each card. Unfortunately I am not able to do it properly.
I am attaching my code here, please someone help me with this.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Task</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<link rel = "stylesheet" href = "css/style.css" type = "text/css">
</head>
<body>
<h2>Creative Handle Task Assignment</h2>
<input type="text" name="text" id="text" placeholder="Enter your text here...">
<button id="btn">Click</button>
<div class="flex-container" id="container">
</div>
<script src="js/custom_script.js" type="text/javascript"></script>
</body>
</html>
style.css
.flex-container {
display: flex;
flex-direction: column-reverse;
justify-content: center;
align-items: center;
}
.flex-container > div {
/*background-color: DodgerBlue;*/
color: white;
width: 100px;
margin: 10px;
text-align: center;
line-height: 75px;
font-size: 30px;
}
custom_script.js
const subBtn = document.getElementById("btn");
const inptTxt = document.getElementById("text");
const contDiv = document.getElementById("container");
subBtn.disabled = true
inptTxt.addEventListener('input', evt => {
const value = inptTxt.value.trim()
if (value) {
inptTxt.dataset.state = 'valid'
subBtn.disabled = false
} else {
inptTxt.dataset.state = 'invalid'
subBtn.disabled = true
}
})
var xhttp = new XMLHttpRequest();
subBtn.addEventListener("click",function(){
var crd = document.createElement("div");
crd.setAttribute("id", "card");
crd.innerHTML = inptTxt.value;
contDiv .appendChild(crd);
xhttp.onreadystatechange=function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("card").style.background = JSON.parse(this.responseText).color;
}
};
xhttp.open("GET","http://api.creativehandles.com/getRandomColor","" ,true);
xhttp.send();
})
Each time you create a new element you give it the id of card. You can't have multiple html elements with the same id. You should use crd.setAttribute("class", "card");' instead. The external stylesheet you load has styling for the class .card but not for id #card.
You can not give id to more one html tag.
Instead of id use class attribute i.e.
crd.setAttribute("class", "card");

upload with multiple image preview

I am using this source: http://opoloo.github.io/jquery_upload_preview/
until now, I can upload one image with preview.
<style type="text/css">
.image-preview {
width: 200px;
height: 200px;
position: relative;
overflow: hidden;
background-color: #000000;
color: #ecf0f1;
}
input[type="file"] {
line-height: 200px;
font-size: 200px;
position: absolute;
opacity: 0;
z-index: 10;
}
label {
position: absolute;
z-index: 5;
opacity: 0.7;
cursor: pointer;
background-color: #bdc3c7;
width: 200px;
height: 50px;
font-size: 20px;
line-height: 50px;
text-transform: uppercase;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
text-align: center;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$("image-preview").each(
function(){
$.uploadPreview({
input_field: $(this).find(".image-upload"),
preview_box: this,
label_field: $(this).find(".image-label")
});
}
);
});
</script>
<!--| catatan penting: yang penting action="" & input type="file" name="image" |-->
<form action="upload.php" method="POST" enctype="multipart/form-data">
<div class="image-preview">
<label for="image-upload" class="image-label">+ GAMBAR</label>
<input type="file" name="my_field[]" class="image-upload" />
</div>
<div class="image-preview">
<label for="image-upload" class="image-label">+ GAMBAR</label>
<input type="file" name="my_field[]" class="image-upload" />
</div>
<input type="submit"/>
</form>
then try to add more div class image preview, i want add another button with image preview. i don't want multiple upload with one button.
$(document).ready(function() {$.uploadPreview => use id, of course when change to class and add more div, when upload a button, another button will change. i am confused with the logic. Anyone can help? maybe using array but, i don't know how..
Since upload button is dependent on state of uploadPreview you need to initialize for each div separately to get separate upload buttons.
Change your html like this give each container a class say imgBox
<div class="imgBox">
<label for="image-upload" class="image-label">Choose File</label>
<input type="file" name="image" class="image-upload" />
</div>
.....
....
...
<div class="imgBox">
<label for="image-upload" class="image-label">Choose File</label>
<input type="file" name="image" class="image-upload" />
</div>
..
Now initialize each one using jquery each()
$(".imgBox").each(
function(){
$.uploadPreview({
input_field: $(this).find(".image-upload"),
preview_box: this,
label_field: $(this).find(".image-label")
});
});
I created a simple image uploading index.html file for image uploading and preview.
Needs j-query.No need of extra plugins.
If you have any questions ask me ;)
//to preview image you need only these lines of code
var imageId=idOfClicked;
var output = document.getElementById(imageId);
output.src = URL.createObjectURL(event.target.files[0]);
Check it here:
https://jsfiddle.net/chs3s0jk/6/
I have one better option for the file upload it's easy to use and you can try it.
window.onload = function(){
if(window.File && window.FileList && window.FileReader){
$(document).on("change",'.file', function(event) {
var files = event.target.files; //FileList object
var output = document.getElementById("upload-preview");
$("#upload-preview").html("");
if(files.length>5){
$(".file").after("<div class='alert alert-error'><span class='close'></span>Maximum 5 files can be uploaded.</div>");
$(this).val("");
return false;
}
else{
$(".file").next(".alert").remove();
}
for(var i = 0; i< files.length; i++)
{
var file = files[i];
//Only pics
// if(!file.type.match('image'))
if(file.type.match('image.*')){
if(this.files[0].size < 2097152){
// continue;
var picReader = new FileReader();
picReader.addEventListener("load",function(event){
var picFile = event.target;
var div = document.createElement("div");
div.className = "upload-preview-thumb";
div.style.backgroundImage = 'url('+picFile.result+')';
output.insertBefore(div,null);
});
//Read the image
$('#clear, #upload-preview').show();
picReader.readAsDataURL(file);
}else{
alert("Image Size is too big. Minimum size is 1MB.");
$(this).val("");
}
}else{
alert("You can only upload image file.");
$(this).val("");
}
}
});
$(".file2").change(function(event){
var err=0;
var input = $(event.currentTarget);
var ele = $(this);
var file = input[0].files[0];
var u = URL.createObjectURL(this.files[0]);
var w = ele.attr("data-width");
var h = ele.attr("data-height");
var img = new Image;
img.onload = function(){
if(w){
if(img.width!=w || img.height!=h){
ele.parent().find(".alert").remove();
ele.parent().find(".upload-preview").before("<div class='alert alert-error'>Please upload a image with specified dimensions.</div>");
ele.val("");
}
else{
ele.parent().find(".alert").remove();
}
}
};
img.src = u;
var nh;
if($(this).attr('data-preview')=='full')
nh = (h/w*150)
else
nh=150
var preview = ele.parent().find(".upload-preview");
var reader = new FileReader();
preview.show();
reader.onload = function(e){
image_base64 = e.target.result;
preview.html("<div class='upload-preview-thumb' style='height:"+nh+"px;background-image:url("+image_base64+")'/><div>");
};
reader.readAsDataURL(file);
});
}
else
{
console.log("Your browser does not support File API");
}
}
above code save as one js file like file-upload.js
then link it to your file where you want perview.
i.e.
<script src="js/file-upload.js" type="text/javascript"></script>
use this kind of example for the input type
<input type="file" class="file2" name="page-image" id="page-image"/>
that works on the class that name is "file2" that class you given to the input field that able to create preview.
full structure something like below.
HTML Code you can try
<input type="file" class="file2" name="page-image[]" id="page-image"/>
<div class="clearfix"></div>
<div class="upload-preview" style="display: block;">
<div class="upload-preview-thumb">
// perview genereate here
// you can display image also here if uploaded throw the php condition in edit image part
</div>
</div>
<input type="file" class="file2" name="page-image[]" id="page-image"/>
<div class="clearfix"></div>
<div class="upload-preview" style="display: block;">
<div class="upload-preview-thumb">
// perview genereate here
// you can display image also here if uploaded throw the php condition in edit image part
</div>
</div>
CSS
.upload-preview {
border: 1px dashed #ccc;
display: block;
float: left;
margin-top: 10px;
padding: 5px;
}
.upload-preview-thumb {
background-position: 50% 25%;
background-size: cover;
float: left;
margin: 5px;
position: relative;
width: 139px;
}
Hope this works and in future it's helpful for you.
Thanks.

How to make html div with text over image downloadable/savable for users?

I have a div that takes a user image and places user text over it. My goal is for the users to, after seeing the preview and customizing the image/text to their like, be able to download or save the image with the click of a button. Is this possible? Here's my code: (I'm new to html/css so please forgive ugly formatting/methods)
HTML:
<script `src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>`
<p>DOM-rendered</p>
<p> </p>
<div id="imagewrap" class="wrap" style="border-style: solid;">
<img src="../images/environment.gif" id="img_prev" width="640" height="640" />
<h3 class="desc">Something Inspirational</h3>
</div>
<div id="canvasWrapper" class="outer">
<p>Canvas-rendered (try right-click, save image as!)</p>
<p>Or, <a id="downloadLink" download="cat.png">Click Here to Download!</a>
</div>
CSS:
.desc {
text-align: center;
}
.outer, .wrap, .html2canvas, .image_text {
display: inline-block;
vertical-align: top;
}
.wrap {
text-align: center;
}
#imagewrap {
background-color: white;
}
JavaScript:
window.onload = function() {
html2canvas(document.getElementById("imagewrap"), {
onrendered: function(canvas) {
canvas.className = "html2canvas";
document.getElementById("canvasWrapper").appendChild(canvas);
var image = canvas.toDataURL("image/png");
document.getElementById("downloadLink").href = image;
},
useCORS: true
});
}
function changePicture(image) {
var at = $(image).attr('at');
var newpath = '../images/' + at;
$("#img_prev").attr('src', newpath);
}
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#img_prev')
.attr('src', e.target.result)
.width(640)
.height(640);
};
reader.readAsDataURL(input.files[0]);
}
};
$(document).on("click", '.font-names li a', function() {
$("#imagewrap h3").css("font-family", $(this).parent().css("font-family"));
$("#new_tile_font_style").val($(this).parent().css("font-family"));
});
$(document).on("click", '.font-sizes li a', function() {
$("#imagewrap h3").css("font-size", $(this).parent().css("font-size"));
$("#new_tile_font_size").val($(this).parent().css("font-size") + "px");
});
$(document).on("click", '.font-colors li a', function() {
$("#imagewrap h3").css("color", $(this).parent().css("color"));
$("#new_tile_font_color").val($(this).parent().css("color"));
});
$("#new_tile_quote").on('keyup', function() {
var enteredText = $("#new_tile_quote").val().replace(/\n/g, "<br>");
$("#imagewrap h3").html(enteredText);
});
What you're trying to accomplish is entirely possible using just HTML, JS, and CSS, with no server-side code. Here is a simplified demo that uses the html2canvas library to render your entire DOM element to a canvas, where the user can then download it.
Be sure to click "Full page" on the demo so you can see the whole thing!
window.onload = function() {
html2canvas(document.getElementById("imagewrap"), {
onrendered: function(canvas) {
canvas.className = "html2canvas";
document.getElementById("canvasWrapper").appendChild(canvas);
var image = canvas.toDataURL("image/png");
document.getElementById("downloadLink").href = image;
},
useCORS: true
});
}
.desc {
text-align: center;
}
.outer, .wrap, .html2canvas, .image_text {
display: inline-block;
vertical-align: top;
}
.wrap {
text-align: center;
}
#imagewrap {
background-color: white;
}
#wow {
color: red;
display: block;
transform: translate(0px, -12px) rotate(-10deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div class="outer">
<p>DOM-rendered</p>
<p> </p>
<div id="imagewrap" class="wrap" style="border-style: solid;">
<img src="https://i.imgur.com/EFM76Qe.jpg?1" id="img_prev" width="170" />
<h3 class="desc">Something <br /><span style="color: blue;">Inspirational</span></h3>
<span id="wow">WOW!</span>
</div>
</div>
<div id="canvasWrapper" class="outer">
<p>Canvas-rendered (try right-click, save image as!)</p>
<p>Or, <a id="downloadLink" download="cat.png">Click Here to Download!</a>
</div>
Here's a quick demo that shows how to use JavaScript to convert your markup into a canvas, then render that into an image and replace it on the page.
document.getElementById('generate').onclick = generateImage;
function generateImage() {
var container = document.getElementById('image_text');
var imgPrev = document.getElementById('img_prev');
var desc = document.getElementById('desc');
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.setAttribute('width', container.clientWidth);
canvas.setAttribute('height', container.clientHeight);
context.drawImage(imgPrev, 0, 0);
context.font = "bold 20px serif";
context.fillText(desc.innerHTML, 0, container.clientHeight-20);
context.strokeRect(0, 0, canvas.width, canvas.height);
var dataURL = canvas.toDataURL();
var imgFinal = new Image();
imgFinal.src = dataURL;
container.parentNode.insertBefore(imgFinal, container.nextSibling);
container.remove();
document.getElementById('generate').remove();
}
#image_text {
display: inline-block;
border: 1px solid #000;
}
<div id="image_text">
<div class="wrap">
<img src= "https://placekitten.com/g/200/300" id="img_prev" crossorigin="anonymous" />
<h3 id="desc" contenteditable>Something Inspirational</h3>
</div>
</div>
<button id="generate">Generate Image</button>
You can replace the image file with anything you like. I've added a crossorigin property to the img tag, and this is because canvases that use resources from other sites cannot be exported unless a crossorigin attribute is specified (if your scripts and images are on the same domain, this is unnecessary).
I've also made the h3 tag editable. You can click on the text and start typing to change what it says, then click "generate image" and save the rendered output.
This script is just a demonstration. It is not bulletproof, it is only a proof-of-concept that should help you understand the techniques being used and apply those techniques yourself.
The javascript creates a canvas element (detached from the DOM), and sizes it according to the container div in your markup. Then it inserts the image into the canvas (it inserts it at the top-left corner), loads the text from your h3 tag and puts it near the bottom-left of the canvas, and converts that canvas to a data-uri. Then it creates a new img element after the container and deletes the container and button.

Categories