jquery append does not work for duplicate image object refernce - javascript

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());

Related

Trying to use different array data structure in a jQuery script

I came across this random image rotator
var theImages = new Array()
theImages[0] = 'img1"' theImages[1] = 'img2"'......
var shuffled = [];
while (theImages.length) {
shuffled.push(theImages.splice(Math.random() * theImages.length, 1));
}
$(document).ready(function(){
//Runs Script each time there is an <li>
$("#news li").each(function(index){
//Puts Image in front of everything in <li>
$(this).prepend('<img src="'+shuffled[index]+'">');
});
});
And I integrated it into a jQuery Carousel. You can see (my fiddle) here. The problem is that the array structure I want to use for the Carousel is different from the rotator's. The rotator uses:
var theImages = new Array()
theImages[0] = 'img1"'
theImages[1] = 'img2"'
theImages[2] = 'img3"'
But I want to use the following for the Carousel because I want to input a link to each picture as well.
var theImages = [
['img1','url_1'],
['img2','url_2'],
['img3','url_3']];
The outcome I'm looking for would be like:
<div id="carousel1">
<div class="1"><img src="img1"></div>
<div class="1"><img src="img2"></div>......
From what I've read, I have to use for in loop to assign the data and can't simply use what the image rotator is using:
$("#carousel1 .1").each(function(index){
$(this).prepend('<a href="'+imgPath+''+shuffled[index]+'"><img
src="'+urlPath+''+shuffled[?url](?url)+'"></a>');
Is that true? Is there any way that allows me to populate the images as well as the urls in the Carousel?
No, never use for ... in for arrays. All you have to do is change
'<img src="'+shuffled[index]+'">'
to
'<img src="'+urlPath+shuffled[index][0]+'">'
since the image path is the first element of the inner array, and the link is the second one.
See Access / process (nested) objects, arrays or JSON for more info.
(I assume imgPath and urlPath are actually defined somewhere and contain URLs. If not, you should remove them.)
Your problem is that your source array (the shuffled values) only contains one value but the final resulting one you want to have pairs of values. That extra data needs to come from somewhere, the computer can't just invent it.
The easiest way to do this is to modify the shuffle code and instead of shuffling single strings shuffle pairs of strings instead.
i.e. something like
var theImages = new Array()
theImages[0] = ['img1"', data1]
theImages[1] = ['img2"', data2]
for(var i=0;i<theImages.length;i++){
newTheImagesArray.push([theImages[i],theImages[i]])
}
This should work for what you want

How to use strings to find and update an object

In javascript, I'm loading an json object, and trying to update values within it. Elements in html have ids that match their path, such that the "values.one" element in this:
{"values":{"one":"val1","two":"val2"},"OtherStuff":"whatever"}
would be related to this element:
<input id="values_one" type="text">val1</div>
When this element loses focus, I want to grab the value of "#values_one" (using jQuery) and set use whatever value is there into my json object. I've figured out how to get the value from the existing json object (see my horrid code below), but I end up with nothing but the value, so setting it doesn't affect the json object. Any suggestions on how I can do this (assuming that I don't know whether the element that lost focus was "values_one", "values_two", or any other element that might map to the json object)?
Here's the code that I have at this point (that isn't working), but happy to scrap it for something that actually works:
var saveEdit = function() {
var data = getJson();
pathElements = $(this).attr('id').split('_');
length = pathElements.length;
var path = data[pathElements[0]];
index = 1;
while (index < length) {
path = path[pathElements[index]];
length -= 1;
}
path = $(this).text(); //resets "path", but not data.values.one
$(this).unbind();
}
Loop one shorter than the length, so that you get the element that contains the property that matches the last identifier:
var path = data;
for (index = 0; index < length - 1; index++) {
path = path[pathElements[index]];
}
path[pathElements[length - 1]] = $(this).text();
Firstly, there's no such thing as a "JSON Object". Yes, I'm being pedantic, but you should either have a "JSON String", or a "Javascript Object".
It looks like you're trying to modify a JSON string via events, instead of just having the object itself available to reference and modify. Why bother keeping it in a string? When you're ready to export the data (perhaps saving to a db), then just stringify(); the object and be on your way.
Take a look at the following jsFiddle for a working implementation that you can build off of: http://jsfiddle.net/julianlam/WRZPF/

How to add an object to a html string?

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.

Javascript Remove Form Data

I have a form that I want to only submit post data for value which have changed.
So the way I have been doing this is like this:
function submit_form(){
var hd = [];
// hd is a big array that is defined here
// hd ['some id_number'] = 'some value'
// main function
for (var id_number in hd ){
var x=document.getElementById(id_number).selectedIndex;
var y=document.getElementById(id_number).options;
selector_text = y[x].text;
if (hd[id_number] == selector_text){
$(id_number).remove();
}
}
document.forms["my_form"].submit()
}
So the goal is that if the selector equals what is in the array, then don't POST the data.
To do this I have been doing the remove function. Everything up to the remove function works as expected. However when I look at the post data I still get the selected value for the id_numbers that mach the value in hd.
Is there a better way to remove to prevent it from going to the POST data? The id.parent.removeChild(id) method didn't work either.
The jQuery id selector should begin with a #, but yours appears not to:
$('#' + id_number).remove();
Your for-in loop should be a regular incremental for loop, which is the proper way to iterate an array in JavaScript. for-in loops are typically used for iterating object properties rather than array elements.
for (var i=0; i<hd.length; i++) {
// Access hd[i] in the loop
var x=document.getElementById(hd[i]).selectedIndex;
var y=document.getElementById(hd[i]).options;
selector_text = y[x].text;
if (hd[i] == selector_text){
$('#' + hd[i]).remove();
}
}
Since you aren't really using jQuery here except for that line, instead the plain JS version is:
var removeMe = document.getElementById(hd[i]);
removeMe.parentNode.removeChild(removeMe);

Dispose an image object

When creating a new Image element in javascript, Google Chrome's memory tool (Developer tools > Timeline > Memory) considers it as a new DOM element, naturally.
In my case, I'm ending up with 1500+ DOM elements, and I wish to get rid of them. I have tried to save all objects in an array and delete all of them in a loop when I'm ready creating all objects, resulting in the following error:
Uncaught TypeError: Cannot call method 'removeChild' of null
That indicates the Image objects doesn't appear in the actual DOM.
var images = [];
var i, image;
for( i = 0; i < urls.length; i++ ) {
image = new Image();
image.src = urls[i];
}
// other stuff happens
for( i = 0; i < images.length; i++ ) {
// apparently this doesn't work because I'm not adding the image to my DOM
// images[i].parentNode.removeChild( images[i] );
// delete images
}
Is there a way to remove/delete/unset/dispose the Image objects?
Setting images = null would remove your reference in code to the object. However, to implement its load event, Chrome has to have its own internal reference to the object.
That is, you could have code like this:
for( i = 0; i < urls.length; i++ ) {
image = new Image();
image.src = urls[i];
image.onload = function(){alert('Test');};
image = null;
}
This way you would still get a lot of "Test" alerts, even though you do not have a reference to these objects.
Hence, my guess is that it is a bug in Chrome, not in your code.
Update: looking through the Chromium source sort of proves that (I mean the comment on lines 67-71 of this file, especially the FIXME note http://code.google.com/searchframe#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/bindings/v8/custom/V8HTMLImageElementConstructor.cpp ):
// Make sure the document is added to the DOM Node map. Otherwise, the HTMLImageElement instance
// may end up being the only node in the map and get garbage-ccollected prematurely.
// FIXME: The correct way to do this would be to make HTMLImageElement derive from
// ActiveDOMObject and use its interface to keep its wrapper alive. Then we would
// remove this code and the special case in isObservableThroughDOM.
If you are not adding them to the DOM (like using appendChild to a parent), then removeChild is useless. The Image objects are only in the memory.
And to dispose items in the memory, you only need to remove references to these objects (like set the referencing variable to null), and garbage collection will do the rest. If you can't null them all, they won't be GC'ed.
To get rid of the bug described by "naivists" of chrome and specilly IE and EDGE.
You can change the image source to empty so it take zero memory.
image.src = '';
image = null;
AFAIK, assigning null should clean it up: images[i] = null
I think only way is to do this:
for( i = 0; i < images.length; i++ )
images[i] = null;
}
// or just
images = null;

Categories

Resources