I new to JavaScript an jQuery and Google doesn't lead to an answer. I am writing a online Ebook reader. This is the code for a library where the user can input multiple epub files and relevant information(like Author) should be displayed in a table. To do this I need to extract the ePub file. The jsZip library works perfect. The contents must be displayed in a table which is dynamically created(since I don't know the amount of files).
The problem is that the for loops is to fast and creates all the cells with only the name and filesize within them and after the for loop completes the onload of the FileReader executes and adds all the contents into the very last cell. In this code the alert("A") happens as many times as files that were inputted before alert("B") happens. Is there some way that I can make the loops wait until the onload of the FileReader is done?
function handleFileSelect(evt) {
var files = evt.target.files;
var rows = Math.ceil(files.length/3);
var a = 0;
var root = document.getElementById('mainTable');
var tab=document.createElement('table');
tab.style.textAlign = "center";
var row, cell;
var tbo=document.createElement('tbody');
for(var i = 0; i != rows; i++)
{
row=document.createElement('tr');
for(var j = 0; (j < 3);j++)
{
cell = document.createElement('td');
cell.height = "300px";
cell.width = "300px"
if(a <indexes.length)
{
var f = files[a];
var str = f.name;
str = str.substring(0, str.length - 5);
str = "File Name: " + str;
cell.appendChild(document.createTextNode(str));
cell.appendChild(document.createElement('br'));
str = "File Size: " + Math.round(f.size/1024) + " KB";
cell.appendChild(document.createTextNode(str));
cell.appendChild(document.createElement('br'));
var reader = new FileReader();
reader.onload = (function(theFile)
{
return function(e)
{
alert("B");
var zip = new JSZip(e.target.result);
$.each(zip.files, function (index, zipEntry)
{
cell.appendChild(document.createTextNode(zipEntry.name));
cell.appendChild(document.createElement('br'));
//row.appendChild(cell);
});
}
})(f);
reader.readAsArrayBuffer(f);
alert("A");
a++;
}
row.appendChild(cell);
}
tbo.appendChild(row);
}
tab.appendChild(tbo);
root.appendChild(tab);
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
Your problem is that the variable cell used within your onload handler will refer to the last value assigned to cell in the outer loop. You can get around this by putting the file processing in a separate function which will create it's own scope.
function read_zip_file(f, cell) {
var reader = new FileReader();
reader.onload = (function(theFile)
{
return function(e)
{
alert("B");
var zip = new JSZip(e.target.result);
$.each(zip.files, function (index, zipEntry)
{
cell.appendChild(document.createTextNode(zipEntry.name));
cell.appendChild(document.createElement('br'));
//row.appendChild(cell);
});
}
})(f);
reader.readAsArrayBuffer(f);
}
Then within the outer loop you can call:
read_zip_file(f, cell);
Related
I am able to display the content from a csv file on a web page ( based on what I have found on this site), but how do I read the csv values into an array in JavaScript?
Let's say if I have a file in this CSV format:
Red, Green, Blue,,
Orange, Yellow, Black,,
Indigo, purple, navy,, ...
I appreciate any help.
function UploadCSV() {
var regex = /^([a-zA-Z0-9\s_\\.\-:])+(.csv|.txt)$/;
if (regex.test($("#fileUpload").val().toLowerCase())) {
if (typeof (FileReader) != "undefined") {
var reader = new FileReader();
reader.onload = function (e) {
try{
var table = $("<table />");
var rows = e.target.result.split("\n");
for (var i = 0; i < rows.length; i++) {
var row = $("<tr />");
var cells = rows[i].split("|");
if (cells.length > 1) {
for (var j = 0; j < cells.length; j++) {
var cell = $("<td />");
var td = cells[j].replace(/[^\x00-\x7F]/g, "");
cell.text(td);
row.append(cell);
}
table.append(row);
}
}
$("#dvCSV").html('');
$("#dvCSV").append(table);
}
catch(e)
{
$('#meessageBar1').text(e.message);
$('#meessageBar1').fadeIn("slow", function () {
setTimeout(messageBar1Remove, 2000);
});
}
}
reader.readAsText($("#fileUpload")[0].files[0]);
} else {
$('#meessageBar1').text('This browser does not support HTML5.');
$('#meessageBar1').fadeIn("slow", function () {
setTimeout(messageBar1Remove, 2000);
});
}
}
}
This is a snippet I used to read data from a PIPE ('|') seperated csv file data into HTML table, you can var cells = rows[i].split("|"); change this line whatever your csv file use as seperator. Here I attach each cell data of each row into of a table row, you can omit this and simply insert whole data into an array. If this helps you, please mark it as accepted answer. Thank you.
In this W3schools example, console.log on the input element reveals a FileInput object:
FileList {0: File, 1: File, length: 2}
How can I work with this? The example demonstrates accessing the file, but every time a user selects new files, the old files disappear. How can I create a new empty FileList and copy it over, so that a user can add more files to the FileList?
I tried this, but it results in two FileList objects, rather than one FileList with all the files:
var fileStore = x.files;
function myFunction(){
var txt = "";
if ('files' in x) {
if (x.files.length == 0) {
txt = "Select one or more files.";
} else {
fileStore += x.files;
console.log(x.files);
console.log(fileStore);
Untested, but this should work
var fileStore = [];
function myFunction(){
var txt = "";
if ('files' in x) {
if (x.files.length == 0) {
txt = "Select one or more files.";
} else {
fileStore.push.apply(fileStore,x.files);
console.log(x.files);
console.log(fileStore);
More on Function::apply
More on Array::push
It is not possible to add File objects to FileList. You can use FormData to append Files to a single object.
var data = new FormData();
document.querySelector("input[type=file]")
.addEventListener("change", function(event) {
for (var i = 0, files = event.target.files; i < files.length; i++) {
data.append("file-" + [...data.keys()].length, files[i], files[i].name)
}
})
An array is fine for holding onto the File instances, but FormData is better if you want to upload them somewhere. If you want to log out or view the FormData, turning it into a Map is an option. Keep in mind that FormData is iterable.
var formData = new FormData();
var index = 0;
function onDrop(event)
{
var dt = event.dataTransfer;
var files = dt.files;
var count = files.length;
output("File Count: " + count + "\n");
for (var i = 0; i < files.length; i++) {
formData.append(files[i].name, files[i]);
}
}
function output(text)
{
document.getElementById("output").textContent += text;
console.dir(new Map(formData));
}
See this JSBin.
it is possible to add files using the datatransfer class
export const makeFileList = files => {
const reducer = (dataTransfer, file) => {
dataTransfer.items.add(file);
return dataTransfer;
}
return files.reduce(reducer, new DataTransfer()).files;
}
I'm trying to read in a file, split the words out into an array, count the length of each word, then reassign each word into a new array if the word is greater than a number entered. When the file is selected, thats when the function should execute, perform the above logic and display the result to an element. The EventListener doesn't seem to be getting triggered when I set a breakpoint in Chrome. Any suggestions?
I've included the JS and html for troubleshooting. Thanks in advance!
function filteredWords() {
var fileInput = document.getElementById('fileInput'); //create a variable for the html input
var displayResult = document.getElementById('displayResult'); //create a variable for the html display
var temp = document.getElementById('num').value;
var num = parseInt(temp, 10);
fileInput.addEventListener('change', function (e) {
var file = fileInput.files[0]; //store the first file into a variable
var textType = /text.*/; //create a variable for checking if file type is text
if (file.type.match(textType)) { //if the file is of type text
var reader = new FileReader(); //create a new file reader object
var text = "";
var wordArray = [];
var filteredArray = [];
reader.readAsText(file); // read the file
reader.onload = function (e) { //for the onload event
text = reader.result; //assign result to new variable
wordArray = text.split(' '); //split the text words into an array
for (i = 0; i < wordArray.length; i++) { //loop through the array and replace largest word with largest in array
if (wordArray[i].length > num) {
filteredArray += wordArray[i];
}
}
displayResult.innerHTML = "Your filtered words are: "; //display the result in the browser element
for (i = 0; i < filteredArray.length; i++) {
displayResult.innerHTML = largestWord + ", ";
}
};
} else { //display a message if file wasn't read
displayResult.innerHTML = "File not supported!";
}
});
}
//inputs
<input class="" type="number" id="num" />
<input type="file" onclick="filteredWords()" id="fileInput" />
//display
<p id="displayResult" class="control-label"></p>
var arrayTest;
function handleFiles(files) {
arrayTest = new Array();
for(var i = 0; files[i]; i++) {
var reader = new FileReader();
reader.onload = function(e) {
var binary = e.target.result;
var parentSelector = '#output_' + i;
$(parentSelector).html(binary);
arraytest.push({ 'bin' : binary, 'parentSelector' : parentSelector });
};
reader.readAsBinaryString(files[i]);
}
}
function buttonClick() {
var files = [file1, file2];
handleFiles(files);
console.log(arrayTest); // I got the objects inserted at async function here, length != 0
console.log(arrayTest.length); // 0
console.log(arrayTest[0]); //undefined
// accesing any member of arrayTest returns undefined
}
FireFox Console output.
The code above shows a Js that converts files into binary string that is ran after a button event and I am having issues with being unable to access the global variable arrayTest that is updated with newly pushed value from the filereader onload event.Is there anyway to fix this problem?
Ok. I realized when I woke up that the point where console.log is executed, the async task might still be running which means that the arrayTest is incomplete, so what I this is what I did to fix the issue.
var arrayTest;
var asyncCount;
function handleFiles(files) {
arrayTest = new Array();
asyncCount = 0;
for(var i = 0; files[i]; i++) {
var reader = new FileReader();
asyncCount += 1;
reader.onload = function(e) {
var binary = e.target.result;
var parentSelector = '#output_' + i;
$(parentSelector).html(binary);
arrayTest.push({ 'bin' : binary, 'parentSelector' : parentSelector });
asyncCount -= 1;
if(asyncCount === 0) {
console.log(arrayTest);
console.log(arrayTest.length);
console.log(arrayTest[0]);
// at this point I am able to access all the array members.
}
};
reader.readAsBinaryString(files[i]);
}
}
function buttonClick() {
var files = [file1, file2];
handleFiles(files);
}
Working on the Custom File upload application. I have 2 major issues:
The following given below code is not Opening the File Dialogue Box for Mozilla and IE.
In Chrome its working, but when I select File on First Click, it never adds file to the body. But in second click it adds the file which was Browse in First Click to the body.
Any help for the above issues will be appreciated.
function perform1Click(node) {
alert("INIT");
var evt = document.createEvent("MouseEvents");
evt.initEvent("click", true, false);
node.dispatchEvent(evt);
alert(3)
getFile(evt);
}
function getFile(event) {
var files = event.target.files;
var totalSize = 0;
if (totalSize > 1024*10) {
alert('Total size exceed 1 Mb.');
return;
}
//alert(files)
//alert(files.length);
for (var i = 0, f; f = files[i]; i++) {
displayFileList(f.name, f.size);
totalSize = totalSize+f.size;
}
}
function displayFileList(name, size) {
if (name != '') {
var top_plugin = document.getElementById('top_plugin');
// create tag
var ptag = document.createElement("p");
// create div
var divBox = document.createElement("div");
divBox.setAttribute('class', 'divBox');
// create input[type='checkbox']
var inputCheckBox = document.createElement("input");
inputCheckBox.setAttribute('type', 'checkbox');
inputCheckBox.setAttribute('id', 'checkboxClass')
// add checkbox to div.
divBox.appendChild(inputCheckBox);
// create text node for divBox and add it to divBox.
var txtNode = document.createTextNode(name);
divBox.appendChild(txtNode)
var sizeDivBox = document.createElement("p");
sizeDivBox.setAttribute('style', 'clear:both; display: inline-block;');
var txtSizeNode = document.createTextNode(size);
sizeDivBox.appendChild(txtSizeNode);
divBox.appendChild(sizeDivBox);
// add divBox to ptag.
ptag.appendChild(divBox);
//ptag.appendChild(divTxt);
// add ptag to top_plugin div.
top_plugin.appendChild(ptag);
}
// if file value is not null, make it blank.
if (name != '')
{
name = '';
}
}
I got the solution for the same problems. Please find below the new code.
function uploadDFiles() {
var file = document.getElementById('_file');
file.click();
try {
file.addEventListener("change", getFileName);
}
catch (e) {
file.attachEvent("onclick", getFileNameOnIE);
alert("Error:: "+e.description);
}
}
function getFileName(event) {
var files = event.target.files;
for (var i = 0, f; f = files[i]; i++) {
var fileName = f.name;
var fileSize = f.size;
var fSize = bytesToSize(fileSize, 2);
displayFileList(fileName, fSize);
}
}
But now I have new problem. This code is not working in IE.For IE i am using attachEvent method and its not working. Please find below the code:
function getFileNameOnIE(event) {
alert(event.type);
var files = event.target;
alert(files.length);
for (var i = 0, f; f = files[i]; i++) {
displayFileList(f.name, f.size);
}
}
Can someone provide me the solution for the same now?
--
Tks
Bharat