I'm trying to load several images by a drop action and than resizing them and adding them as a thumbnail. The resize part is very important because the images can be very large and I want to make the thumbnails small of size.
Here is my code:
loadingGif(drop);
for (var i=0;i<files.length;i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = function(e) {
var src = e.target.result;
var img = document.createElement('img');
img.src = src;
var scale = 100/img.height;
img.height = 100;
img.width = scale*img.width;
output.push('<div id="imagecontainer"><div id="image">'+img+'</div><div id="delimage"><img src="img/del.jpg"" /></div></div>');
if(output.length == files.length) {
drop.removeChild(drop.lastChild);
drop.innerHTML += output.join('');
output.length = 0;
}
}
reader.readAsDataURL(file);
}
As you can probably tell I insert a loading gif image in my dropzone until all files are loaded (output.length == files.length). When a file is loaded I add html to an array which I will print if the load is complete. The problem is I can't seem to add the img object (I need this object to resize the image) to the html string, which seems obvious as the img is an object... So my question to you guys is: how do I do this? :)
Generally speaking, it's bad habit to play with DOM elements as strings. You should instead use the native javascript (or the library/framework you are using) commands to create the elements and set their attributes.
For example if you have an element with id "image-container" which contains all your images, you may write.
var container = document.getElementById( 'image-container' );
var img = document.createElement( 'img' );
container.appendChild( img );
Instead of a large HTML string containing all your output, you can then have only a reference to an element containing all the other elements, and either appending it to the body as soon as all the elements have been loaded, or keeping it hidden and showing it when the loading is complete.
if (output.length == files.length) {
drop.removeChild(drop.lastChild);
drop.appendChild( container );
output.length = 0;
}
Remember IDs have to be unique. There cannot be more than one element with the same ID in the same document.
HTML is parsed into elements when inserted onto the page, but that doesn't mean the two formats 1 to 1 translatable.
You could use outerHTML to translate the <img> element to a HTML string. But this wont be efficient. You are translating an element to a string only to be parsed as an element again. Kinda sloppy.
"<div id='image'>"+ img.outerHTML +"</div>"
You could build the <img> tag as a string.
"<div id='image'><img src='"+ src +"'></div>"
But that starts to get hairy too. So lastly you could append the image element after you create the other html.
drop.innerHTML = "<div class='image'></div>"
document.querySelectorAll('.image:last-child')[0].appendChild(img);
However, food for thought: when you start having this much HTML in your JS, you may want to rethink your approach a bit. It's a hard to maintain and very human error prone pattern.
As others have said, there are reasons why one would avoid/choose to use strings to represent DOM elements. Also, it's a bit of a wtf moment reading code that attempts to mix both methods of representing html elements. that said, you can skip creating the image as an element in and of itself, instead inserting the appropriate string into your result(stack?).
This would have the desired scaling effect:
var src = e.target.result;
var imgStr = "<img src='" + src + "' height='" + 100 + "'/>";
ouput.push('<div id="imagecontainer"><div id="image">'+imgStr+'</div><div id="delimage"><img src="img/del.jpg"" /></div></div>');
You can create an object with arbitrary properties, in order to hold both the image and your HTML string:
var result = {
html: '<div id="imagecontainer"><div id="image">'+img+'</div><div id="delimage"><img src="img/del.jpg"" /></div></div>',
imgTag: img
}
output.push(result);
And then later on when you iterate through the results array, you can access these parts as result.html or result.imgTag (changing the first part to whatever variable you've assigned the object to).
It does mean that you'd need to change your current output.join('') to a loop that actually iterates through the values of output and concatenates their html properties instead. If you really wanted to avoid this, you could stick the images in a separate output-style array. But this isn't as clean, and runs the risk of the two array contents becoming out-of-sync. I'd stick to having two separate properties of the same object, as cleaner and better.
Related
I'm trying to incorporate some images "dynamically" by saving them as variables in an array and then editing the innerHtml of some divs to include them, but the images aren't showing. I'm using img.src = varName; Here's a link to the code: https://repl.it/GBoa/1 (here's also a link to a website version where the images were uploaded: cardtestwdi.bitballoon.com). I would appreciate any help I could get.
If you want to set innerHTML using a string, you will have to provide the full HTML string. You can't create a string and then use properties for regular DOM nodes. For example, you could use the following:
var imgElement = "<img src='" + myArray[i] + "' alt='test' width='200px' height='275px'/>";
However instead of doing this I would recommend creating the node in JavaScript using document.createElement and append it to the DOM using document.appendChild:
var imgElement = document.createElement('img');
imgElement.width = 200;
imgElement.height = 275;
imgElement.src = myArray[i];
myCard[i].appendChild(imgElement); // Append the image node to the card node
I would like to create an HTML image element like this one:
<img src="www.example.com?param1=aid¶m2=sid¶m3=user¶m4=now" />
I tried doing this:
var now = new Date().getTime();
var aid = 123456;
var sid = 78910;
var user = "abcd";
How can I create the element from this data?
You create an img element (not "tag") using document.createElement('img').
You set its src, a string, using the src reflected property of that element. To create that string, for now, you'd use string concatenation (+). See below for an ES6 note, however.
So:
var img = document.createElement('img');
img.src = "www.example.com?param1=" + aid +
"¶m2=" + sid +
"¶m3 = " + encodeURIComponent(user) +
"¶m4=" + now;
Then you'd want to append that to the DOM somewhere.
Note the encodeURIComponent on the non-numeric one. Both the names and values in a query string must be URI-encoded. But I haven't bothered on param1, param2, etc. because I know that the URI-encoded version of them is...exactly the same. Similarly I know that the URI-encoded version of a number is just the number. But I see user is a text value, and I assume it isn't always "abcd", so to guard against issues I've URI-encoded it.
Re your comment:
And presumably if I'd like to add attributes to the img element it'd be like img.height=1 and img.width=1?
The specification lists the properties of img elements. Yes, both height and width are there and setting them has the same effect as using the height and width attributes, although you might want to use a stylesheet instead.
Not all attributes have reflected properties. For those that don't, you'd use setAttribute("name", value) (the value will be converted to a string if it isn't already one).
As of the next version of JavaScript, ECMAScript6 (ES6), you'd be able to use a template string for src instead:
var img = document.createElement('img');
img.src = `www.example.com?param1=${aid}¶m2=${sid}¶m3=${encodeURIComponent(user)}¶m4=${now}`;
Strings in JS can be chained together with the + operator. Number values will be coerced to strings (although that sometimes won't work as expected).
So I've read that jQuery uses document fragments internally to make rendering faster. But I am wondering if anyone knows if jQuery would use createDocumentFragment in this situation where I'm appending img elements to the DOM using the each loop?
var displayArray = []; // Lots of img elements
$.each(displayArray, function()
{
$('#imgSection').append(this);
});
Or would I need to use this code in order to reduce the number of browser reflows?
var displayArray = []; // Lots of img elements
var imgHolder = $('<div/>');
$.each(displayArray, function()
{
imgHolder.append(this);
});
$('#imgSection').append(imgHolder);
Also, the displayArray is populated by other code, not shown here, that creates img elements based off of paths in a JSON file.
Thank you for any advice.
Why all the looping to add elements?
$('#imgSection').append("<div>" + displayArray .join("") + "</div>");
Okay so it is elements.
The quickest way is going to be using append with the array itself.
$("#out").append(elems);
other option using one div to append is
var div = $("<div/>").append(elems);
$("#out").append(div);
BUT appending a lot of images at once is going to be bad unless they are preloaded. That will be a bunch of http requests being queued up.
jsPerf test cases
No, if you use $.each() then jQuery won't use a DocumentFragment - jQuery has no way of knowing what you're going to do inside the loop and each iteration is independent.
The point of the document fragment is that you don't have to wrap all your new elements up in a wrapper element as you've done in your second example to limit the reflows.
jQuery apparently will use a document fragment if you pass an array of elements directly to .append() instead of iterating over them yourself.
If you really care about reflows (and have noticed the displaying to be slow), you can hide and show the image-holding element:
var displayArray = […]; // Lots of img elements
var holder = $('#imgSection').hide();
for (var i=0; i<displayArray.length; i++)
holder.append(displayArray[i]);
holder.show();
I have an 10 images,
each image represents digit 0-9 in a special font (thus the images)
in order to improve performance and delay, i pre-loaded the following images like the following:
function createDigit() {
for (var i = 0; i < 10; i++) {
var obj = new Image;
obj.src = 'digit' + i + '.png';
digitHash[i] = obj;
}
}
so in digit hash, i have keys indexed from 0 to 9, and each corresponding value is the image object reference, which src is mapped to the image file location.
now in my html, i have a div tag
<div id='digits'></div>
now say i want to display '2000'
so i have the following jquery
$('#digits').append(dightHash[2], dightHash[0], dightHash[0], dightHash[0]);
it only displays '20'
After some debugging and printing in firefox console, i notice that it happens when you are appending the SAME image reference more than once!
in other words, the second zero and third zero in '2000' are not appended and thus we only have '20'
if i append the following:
$('#digits').append(dightHash[2], dightHash[3], dightHash[4], dightHash[5]);
i get the full display of '2345', becauase there is no duplicate image reference in append
How can I resolve this issue?
is there any other than append method of jquery i can use??
Thanks
As had already been explained, .append() moves an object from wherever it is to the specified location. It does not make a copy of the object.
Because of that, I would suggest you just create the desired objects like this and then you don't have to worry about duplicate digits as they will each get their own image object this way:
// create an individual image
function makeDigit(n) {
var img = new Image();
img.src = 'digit' + n + '.png';
return(img);
}
// Force all images into browser memory cache for fast loading:
function cacheDigits() {
for (var i = 0; i < 10; i++) {
digitHash.push(makeDigit(i));
}
}
$('#digits').append(makeDigit(2), makeDigit(0), makeDigit(0), makeDigit(0));
Yes, append actually move around your DOM, instead of automatically making copies of the object you are appending.
You can call .clone() so that appends take the copy of your image and append it instead of moving around the ref
$('#digits').append(dightHash[2], $(dightHash[0]).clone(), $(dightHash[0]).clone(), $(dightHash[0]).clone());
So I am grabbing RSS feeds via AJAX. After processing them, I have a html string that I want to manipulate using various jQuery functionality. In order to do this, I need a tree of DOM nodes.
I can parse a HTML string into the jQuery() function.
I can add it as innerHTML to some hidden node and use that.
I have even tried using mozilla's nonstandard range.createContextualFragment().
The problem with all of these solutions is that when my HTML snippet has an <img> tag, firefox dutifully fetches whatever image is referenced. Since this processing is background stuff that isn't being displayed to the user, I'd like to just get a DOM tree without the browser loading all the images contained in it.
Is this possible with javascript? I don't mind if it's mozilla-only, as I'm already using javascript 1.7 features (which seem to be mozilla-only for now)
The answer is this:
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(htmlString, "text/html");
var jdoc = $(htmlDoc);
console.log(jdoc.find('img'));
If you pay attention to your web requests you'll notice that none are made even though the html string is parsed and wrapped by jquery.
The obvious answer is to parse the string and remove the src attributes from img tags (and similar for other external resources you don't want to load). But you'll have already thought of that and I'm sure you're looking for something less troublesome. I'm also assuming you've already tried removing the src attribute after having jquery parse the string but before appending it to the document, and found that the images are still being requested.
I'm not coming up with anything else, but you may not need to do full parsing; this replacement should do it in Firefox with some caveats:
thestring = thestring.replace("<img ", "<img src='' ");
The caveats:
This appears to work in the current Firefox. That doesn't meant that subsequent versions won't choose to handle duplicated src attributes differently.
This assumes the literal string "general purpose assumption, that string could appear in an attribute value on a sufficiently...interesting...page, especially in an inline onclick handler like this: <a href='#' onclick='$("frog").html("<img src=\"spinner.gif\">")'> (Although in that example, the false positive replacement is harmless.)
This is obviously a hack, but in a limited environment with reasonably well-known data...
You can use the DOM parser to manipulate the nodes.
Just replace the src attributes, store their original values and add them back later on.
Sample:
(function () {
var s = "<img src='http://www.google.com/logos/olympics10-skijump-hp.png' /><img src='http://www.google.com/logos/olympics10-skijump-hp.png' />";
var parser = new DOMParser();
var dom = parser.parseFromString("<div id='mydiv' >" + s + "</div>", "text/xml");
var imgs = dom.getElementsByTagName("img");
var stored = [];
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
stored.push(img.getAttribute("src"));
img.setAttribute("myindex", i);
img.setAttribute("src", null);
}
$(document.body).append(new XMLSerializer().serializeToString(dom));
alert("Images appended");
window.setTimeout(function () {
alert("loading images");
$("#mydiv img").each(function () {
this.src = stored[$(this).attr("myindex")];
})
alert("images loaded");
}, 2000);
})();