Skip to content

Commit 7ade796

Browse files
committed
added readme
1 parent 28a74fe commit 7ade796

File tree

4 files changed

+292
-4
lines changed

4 files changed

+292
-4
lines changed

each day build day!/Day 11/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Drag & Drop images with vanilla JS
2+
3+
This projects aims to utilize the <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API">`Drag and Drop api` </a> It use four events `dragenter` `dragleave` `dragover` `drop`
4+
to handle files. As a fallback it use `input type = file`. It can handle multiple files at time, with letting user preview it before the upload. It is used by various browser based tools like imgeditor, canva etc.
5+
6+
7+
# Challenges
8+
- Drag and Drop Api
9+
- file previewing
10+
- Ajax
11+
- Async file handling
12+
- cross browser file upload
13+

each day build day!/Day 11/app.js

Lines changed: 228 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,230 @@
11
const dropRegion = document.getElementById('drop-region');
2-
const previewRegion = document.getElementById('image-preview');
2+
const imagePreviewRegion = document.getElementById('image-preview');
3+
const overlay = document.createElement("div");
4+
let imagetoUpload;
5+
// open file selector when clicked on the drop region
6+
const fakeInput = document.createElement("input");
7+
fakeInput.type = "file";
8+
fakeInput.accept = "image/*"; //multiple values like image/png, image/jpeg.
9+
fakeInput.multiple = true;
310

11+
12+
dropRegion.addEventListener('click', function () {
13+
fakeInput.click();
14+
});
15+
16+
fakeInput.addEventListener("change", function () {
17+
const files = fakeInput.files;
18+
handleFiles(files);
19+
});
20+
21+
22+
23+
function preventDefault(e) {
24+
e.preventDefault();
25+
e.stopPropagation();
26+
}
27+
28+
/*
29+
The Drag & Drop API defines 8 events: 4 events for the draggable element and 4 events for the droppable element. We will only need the latter 4 when developing a drag & drop image uploading.
30+
31+
dragenter: a dragged item enters a valid drop target.
32+
dragleave: a dragged item leaves a valid drop target.
33+
dragover: a dragged item is being dragged over a valid drop target. Triggered every few hundred milliseconds.
34+
drop: an item is dropped on a valid drop target.
35+
36+
*/
37+
dropRegion.addEventListener('dragenter', preventDefault, false);
38+
dropRegion.addEventListener('dragleave', preventDefault, false);
39+
dropRegion.addEventListener('dragover', preventDefault, false);
40+
dropRegion.addEventListener('drop', preventDefault, false);
41+
42+
//handle drop event
43+
function handleDrop(e) {
44+
const dt = e.dataTransfer,
45+
files = dt.files;
46+
47+
if (files.length) {
48+
49+
handleFiles(files);
50+
51+
} else {
52+
53+
// check for img
54+
const html = dt.getData('text/html'),
55+
match = html && /\bsrc="?([^"\s]+)"?\s*/.exec(html),
56+
url = match && match[1];
57+
58+
59+
60+
if (url) {
61+
uploadImageFromURL(url);
62+
return;
63+
}
64+
65+
}
66+
67+
68+
function uploadImageFromURL(url) {
69+
let img = new Image;
70+
let c = document.createElement("canvas");
71+
let ctx = c.getContext("2d");
72+
73+
img.onload = function () {
74+
c.width = this.naturalWidth; // update canvas size to match image
75+
c.height = this.naturalHeight;
76+
ctx.drawImage(this, 0, 0); // draw in image
77+
c.toBlob(function (blob) { // get content as PNG blob
78+
79+
// call our main function
80+
handleFiles([blob]);
81+
82+
}, "image/png");
83+
};
84+
img.onerror = function () {
85+
alert("Error in uploading");
86+
}
87+
img.crossOrigin = ""; // if from different origin
88+
img.src = url;
89+
}
90+
91+
}
92+
93+
dropRegion.addEventListener('drop', handleDrop, false);
94+
95+
/*
96+
handleFiles() function which gets a File List and upload each item.
97+
98+
*/
99+
function handleFiles(files) {
100+
for (let i = 0, len = files.length; i < len; i++) {
101+
if (validateImage(files[i]))
102+
previewImage(files[i]);
103+
104+
}
105+
106+
imagetoUpload = [...files];
107+
}
108+
109+
/*
110+
check if the file is 'MIME' type and file size is < 10MB
111+
--> validation on client-side equals better user XP
112+
*/
113+
function validateImage(image) {
114+
// check the type
115+
const validTypes = ['image/jpeg', 'image/png', 'image/gif'];
116+
if (validTypes.indexOf(image.type) === -1) {
117+
alert("Invalid File Type");
118+
return false;
119+
}
120+
121+
// check the size
122+
const maxSizeInBytes = 10e6; // 10MB
123+
if (image.size > maxSizeInBytes) {
124+
alert("File too large");
125+
return false;
126+
}
127+
128+
return true;
129+
}
130+
131+
132+
/*
133+
Previewing
134+
1. after upload
135+
2. before upload -> fast and efficient
136+
*/
137+
138+
function previewImage(image) {
139+
140+
// container
141+
const imgView = document.createElement("div");
142+
imgView.className = "image-view";
143+
imagePreviewRegion.appendChild(imgView);
144+
145+
// previewing image
146+
const img = document.createElement("img");
147+
imgView.appendChild(img);
148+
149+
// progress overlay
150+
151+
overlay.className = "overlay";
152+
imgView.appendChild(overlay);
153+
154+
// read the image...
155+
const reader = new FileReader();
156+
reader.onload = function (e) {
157+
img.src = e.target.result;
158+
}
159+
reader.readAsDataURL(image);
160+
161+
//Uploading to the server, create a FormData Object and use Ajax or fetch
162+
163+
return image;
164+
}
165+
166+
167+
168+
function upload(imagetoUpload){
169+
console.log('function called')
170+
// create FormData
171+
const formData = new FormData();
172+
formData.append('image', imagetoUpload);
173+
174+
// upload the image
175+
const uploadLocation = 'UPLOAD_LOCATION';
176+
177+
const ajax = new XMLHttpRequest();
178+
ajax.open("POST", uploadLocation, true);
179+
180+
ajax.onreadystatechange = function(e) {
181+
if (ajax.readyState === 4) {
182+
if (ajax.status === 200) {
183+
// done!
184+
} else {
185+
// error!
186+
}
187+
}
188+
}
189+
190+
ajax.upload.onprogress = function(e) {
191+
192+
// change progress
193+
// (reduce the width of overlay)
194+
195+
const perc = (e.loaded / e.total * 100) || 100,
196+
width = 100 - perc;
197+
198+
overlay.style.width = width;
199+
}
200+
201+
ajax.send(formData);
202+
}
203+
204+
205+
//for detecting file upload
206+
207+
dropRegion.addEventListener('dragenter', highlight, false);
208+
dropRegion.addEventListener('dragover', highlight, false);
209+
dropRegion.addEventListener('dragleave', unhighlight, false);
210+
dropRegion.addEventListener('drop', unhighlight, false);
211+
212+
function highlight() {
213+
dropRegion.classList.add('highlighted');
214+
}
215+
function unhighlight() {
216+
dropRegion.classList.remove("highlighted");
217+
}
218+
219+
220+
//for no bowser support
221+
function detectDragDrop() {
222+
const div = document.createElement('div');
223+
return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)
224+
}
225+
226+
// change the message
227+
const dragSupported = detectDragDrop();
228+
if (!dragSupported) {
229+
document.getElementsByClassName("drop-message")[0].innerHTML = 'Click to upload';
230+
}

each day build day!/Day 11/index.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>Deag & Drop File upload 📁 </title>
6+
<title>Drag & Drop File upload 📁 </title>
7+
<link rel="stylesheet" href="style.css">
78
</head>
89
<body>
910

1011
<div class="container">
1112
<h1>Drag & Drop file upload using vanilla JS 📁 </h1>
1213

1314
<div id="drop-region">
15+
1416
<div class="drop-message">Drag & Drop files or click to upload</div>
1517
<div id="image-preview"></div>
1618
</div>
17-
19+
<button class="btn" onclick="upload()">Upload 🔼 </button>
1820
</div>
1921

2022
<script src="app.js"></script>

each day build day!/Day 11/style.css

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
:root{
2-
font-size: 62.5%;
2+
font-size:80%;
3+
background-color: rgb(64, 66, 66);
34
}
45
.container{
56
min-height: 30rem;
@@ -8,4 +9,49 @@
89
justify-content: start;
910
align-items: center;
1011
}
12+
.btn{
13+
margin-top: 20px;
14+
background-color: grey;
15+
border: none;
16+
cursor: pointer;
17+
}
18+
#drop-region {
19+
background-color: #fff;
20+
border-radius:20px;
21+
box-shadow:0 0 35px rgba(0,0,0,0.05);
22+
width:400px;
23+
padding:60px 40px;
24+
text-align: center;
25+
cursor:pointer;
26+
transition:.3s;
27+
}
28+
#drop-region:hover {
29+
box-shadow:0 0 45px rgba(0,0,0,0.1);
30+
}
31+
32+
#image-preview {
33+
margin-top:20px;
34+
}
35+
#image-preview .image-view {
36+
display: inline-block;
37+
position:relative;
38+
margin-right: 13px;
39+
margin-bottom: 13px;
40+
}
41+
#image-preview .image-view img {
42+
max-width: 100px;
43+
max-height: 100px;
44+
}
45+
#image-preview .overlay {
46+
position: absolute;
47+
width: 100%;
48+
height: 100%;
49+
top: 0;
50+
right: 0;
51+
z-index: 2;
52+
background: rgba(255,255,255,0.5);
53+
}
1154

55+
.highlighted {
56+
background-color:grey;
57+
}

0 commit comments

Comments
 (0)