I have a problem calling the onload() function of a new image object when that image is immediately set as an object property. My parser tells me that imageMap[featureId].onload is not a function. I don't get why the imageMap[featureId] isn't synonymous with the image itself? The resourceList is used as an argument to preloadResource(). I have a setinterval elsewhere waiting for the loaded count to equal the resource count. Any ideas please?
var resourceList = ["path.png","path.png","path.png","path.png"];
var loadedResource = 0;
function preloadResource(resArr)
{
var buildIndex = 1;
for (i = 1; i <= resArr.length; i++)
{
var featureId = "feature" + buildIndex;
imageMap[featureId] = new Image();
imageMap[featureId].path = resArr[(i - 1)];
buildIndex++;
imageMap[featureId].onload(function () {loadedResource++;})
imageMap[featureId].src = resArr[(i -1)];
}
}
The error is telling you the problem: "onload is not a method."
imageMap[featureId].onload = function () {loadedResource++;};
or use addEventListener
Related
The following renderChat function is used to render a message and an image onto a chat board. Inside the function there is another function
var onComplete = function () {
which does all the work of creating the list element and appending it to the chat list. After the onComplete function, there is only this three lines of code
img.onload = onComplete;
img.onerror = onComplete;
img.src = c.chat.value.media;
Because the var onComplete is a function assigned to a variable, I assumed it had to be called with parenthesis. Thus, when I see this
img.onload = onComplete;
I understand that the function has been assigned to a new variable, but has never been called. Yet, when I use the application, the chat has been rendered by the time we reach img.src = c.chat.value.media;
Can you please explain how my understanding of JavaScript is mistaken and how this function is working?
var renderChat = function (c) {
debug("Rendering chat: key='%s' fingerprint='%s' message='%s' created='%s' imageMd5='%s'",
c.chat.key,
c.chat.value.fingerprint,
c.chat.value.message,
c.chat.value.created,
md5(c.chat.value.media));
var renderFP = c.chat.value.fingerprint;
if (!isMuted(renderFP)) {
var img = new Image();
var onComplete = function () {
// Don't want duplicates and don't want muted messages
if (body.find('li[data-key="' + c.chat.key + '"]').length === 0 &&
!isMuted(renderFP)) {
var li = document.createElement('li');
li.dataset.action = 'chat-message';
li.dataset.key = c.chat.key;
li.dataset.fingerprint = renderFP;
li.appendChild(img);
// This is likely your own fingerprint so you don't mute yourself. Unless you're weird.
if (userId.val() !== renderFP) {
updateNotificationCount();
var btn = document.createElement('button');
btn.textContent = 'mute';
btn.className = 'mute';
li.appendChild(btn);
}
var message = document.createElement('p');
message.textContent = c.chat.value.message;
message.innerHTML = transform(message.innerHTML);
li.appendChild(message);
var createdDate = moment(new Date(c.chat.value.created));
var timestamp = document.createElement('time');
timestamp.setAttribute('datetime', createdDate.toISOString());
timestamp.textContent = createdDate.format('LT');
timestamp.className = 'timestamp';
li.appendChild(timestamp);
var size = addChat.is(":visible") ? addChat[0].getBoundingClientRect().bottom : $(window).innerHeight();
var last = chatList[0].lastChild;
var bottom = last ? last.getBoundingClientRect().bottom : 0;
var follow = bottom < size + 50;
chatList.append(li);
setupWaypoints(li);
debug('Appended chat %s', c.chat.key);
// if scrolled to bottom of window then scroll the new thing into view
// otherwise, you are reading the history... allow user to scroll up.
if (follow) {
var children = chatList.children();
if (children.length > CHAT_LIMIT) {
children.first().remove().waypoint('destroy');
}
li.scrollIntoView();
}
}
};
img.onload = onComplete;
img.onerror = onComplete;
img.src = c.chat.value.media;
}
};
The HTMLImageElement object will cause functions assigned to its onload and onerror properties to be called at the appropriate times (i.e. when the HTTP response is received or the wait for it times out).
The code to do this is built into the browser. The properties (or the addEventListener function in more modern code) are the only ways with which you can interact with that code.
In Javascript you can store functions in variables, this is what you did with onComplete().
The img object will execute a function(so called callback) after successfully loading the image (onload) or if it could not load the image(onerror).
To tell the img object which method to call after those events, you need to give it the method name like this without parenthesis:
img.onload = onComplete;
img.onerror = onComplete;
If you would use parenthesis the function would be executed immediately and img.onload wouldn't contain a reference to a function but the result of onCompleted.
img.onload = onComplete; will assign the function onComplete to the onload handler. THis means, that the function is called, when the vent happens.
img.onload = onComplete(); will assign the result of calling the function onComplete to the onload handler. This means, that the function is called immediately, is expected to return another function (or a string containing valid JS), which in turn will be called when the event happens.
I am loading a json file and parsing it into an array in Javascript. One of the elements is the path to an image. I am not ready to load the images yet but I need to get the image's height. I know how to do that with code like the following (found on other stackoverflow pages)
function getimageheight(img) {
var tmpImg = new Image();
tmpImg.onload = function() {
var ht = this.height;
return ht+0;
}
tmpImg.src = img;
}
If I try to call this function in a loop, it returns undefined because the onload for the images is running slower than the loop. My actual code is this:
var j = 0;
$.each(cat.placemarks, function(index, mark) {
markers[cat.name][j] = [];
markers[cat.name][j].name = mark.name;
markers[cat.name][j].title = mark.title;
markers[cat.name][j].markerURL = mark.markerURL;
markers[cat.name][j].imageURL = mark.imageURL;
markers[cat.name][j].imageHEIGHT = getimageheight(projpath+mark.imageURL);
j++;
});
If I call the function once, it works. But calling it in a loop does not. How can I fix this?
If you store reference to data object in Img object used to load it, you can set the value of its properties after the loading is done. Hope that makes sense... Your data will not be ready to use before loading is complete tho. Heres the code
var total=cat.placemarks.length;//if an array, otherwise use another each cycle to get object count
var loaded=0;
$each(cat.placemarks, function(index, mark) {
markers[cat.name][j] = [];
var tmpImg = new Image();
tmpImg.refToObjWithNameOfYourChoice=markers[cat.name][j];
tmpImg.onload = function() {
this.refToObjWithNameOfYourChoice.imageHEIGHT=this.heigh;
loaded++;
if(loaded==total){
//markers data is ready to use - add function callback herer or sumthin'
}
}
tmpImg.src=projpath+mark.imageURL;
markers[cat.name][j].name = mark.name;
markers[cat.name][j].title = mark.title;
markers[cat.name][j].markerURL = mark.markerURL;
markers[cat.name][j].imageURL = mark.imageURL;
j++;
});
markers[cat.name][j].imageHEIGHT is undefined because getImageHeight() isn't returning anything. And naturally, the image load will occur much more slowly than your each() loop, so it won't do you any good to have getImageHeight() return something. You'll have to set up your load() callback to determine which image has been loaded and update the height of the corresponding markers element.
CI have this code:
for(var i = 0; i < toObserve.length; i++) {
var elems = toObserve[i].split('###');
var elementToObserve = elems[0];
var imageToUse = elems[1];
$(elementToObserve).observe('click', respondToClick);
}
function respondToClick(event) {
var element = event.element();
}
In the respondToClick function I need a different image (imageToUse) for each elementToObserve. How can I do that? Can I pass a param or something?
Thanks!
Addition: I tried what Diodeus suggested, but it seems that only the last passed parameter is used, when any of the elements I observe is clicked. Whats wrong or is the way I want to do it not the right one?
Use an anonymous function:
$(elementToObserve).observe('click', function(event) {
var yourVar = "moo";
respondToClick(event,yourVar)
});
Use the specialised bindAsEventListener.
$(elementToObserve).observe('click',
respondToClick.bindAsEventListener(imageToUse)
);
The bound arguments will then be passed after the event parameter.
function respondToClick(event, imageToUse)
{
var element = event.element();
element.src = imageToUse;
}
I got this to work by simply assigning my variable to the element object:
elementToObserve.imageToUse = imageToUse;
Then in the observer function:
function respondToClick(event) {
var imageToUse = event.target.imageToUse;
}
I haven't realized any drawbacks to this.
I have a little problem with a homemade script, first I'll give you the script
var heighti = $(window).height();
var imageLoading = new Array();
$('.fullHeight').css({'height' : heighti});
var now,hour,minute,second,dateNow = new Array(),countImg=0,i=0,countDateNow = 0;countImg=0,countThis=0,countDateNowAj=0;
/* GET THIS HOUR */
now = new Date();
hour = now.getHours();
minute = now.getMinutes();
second = now.getSeconds();
function date(){
//Function to get date
}
function loadImage(){
countThis = 0;
while(countThis < 6){
date();
var imgOn = 'uploads/original/'+dateNow[countDateNowAj]+'.jpg';
console.log(imgOn);
var img = $("<img />").attr('src', imgOn)
.load(function(){
imageLoading[i] = imgOn ;
i++;
})
.error(function(){
console.log('This is the image now : '+imgOn);
imageLoading[i] = 'images/noimage.jpg';
i++;
});
countThis++;
countDateNowAj++;
}
}
setInterval("dateImg()",1000);
setTimeout("loadImage()",0);
setInterval("loadImage()",5000);
So this is my function, everything works but when I want to do imageLoading[i] = imgOn; the script take always the last value.
This is a log of what I'm talking about : http://minus.com/mpWvBsXkQ
First I check the time
After I check the image who is gonna be checked
And at the end I check the name of imageLoading[i] = imgOn;
And I always get the last time and not every second.
I hope you'll understand my query.
In the load and error handler functions, you are using variables from the outer scope (in this case the scope will be the loadImage function) asynchronously, but they will be changed synchronously as part of the loop. If you want to have them held constant until the handlers are actually called, you will need to use a closure:
function loadImage(){
function imageLoader(i, imgOn) {
console.log(imgOn);
var img = $("<img />").attr('src', imgOn)
.load(function(){
imageLoading[i] = imgOn ;
})
.error(function(){
console.log('This is the image now : '+imgOn);
imageLoading[i] = 'images/noimage.jpg';
});
}
for(countThis = 0; countThis < 6; countThis++, countDateNowAj++) {
date();
imageLoader(i++, 'uploads/original/'+dateNow[countDateNowAj]+'.jpg');
}
}
In this case, the imageLoader inner function becomes the scope that the load and error handlers run in, so the values of i and imgOn are as you would expect rather than always being the last values they had when the loop finished running.
Declare the imageLoading outside the loop like
var imageLoading = [];
I am having trouble with JS closures:
// arg: an array of strings. each string is a mentioned user.
// fills in the list of mentioned users. Click on a mentioned user's name causes the page to load that user's info.
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// cause the page to load info for this screen name
newAnchor.onclick = function () { loadUsernameInfo(mentions[i]) };
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", ""); // unhide. hacky hack hack.
}
Unfortunately, clicking on one of these anchor tags results in a call like this:
loadUserNameInfo(undefined);
Why is this? My goal is an anchor like this:
<a onclick="loadUserNameInfo(someguy)">someguy</a>
How can I produce this?
Update This works:
newAnchor.onclick = function () { loadUsernameInfo(this.innerHTML) };
newAnchor.innerHTML = mentions[i];
The "i" reference inside the closure for the onclick handlers is trapping a live reference to "i". It gets updated for every loop, which affects all the closures created so far as well. When your while loop ends, "i" is just past the end of the mentions array, so mentions[i] == undefined for all of them.
Do this:
newAnchor.onclick = (function(idx) {
return function () { loadUsernameInfo(mentions[idx]) };
})(i);
to force the "i" to lock into a value idx inside the closure.
Your iterator i is stored as a reference, not as a value and so, as it is changed outside the closure, all the references to it are changing.
try this
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// Set the index as a property of the object
newAnchor.idx = i;
newAnchor.onclick = function () {
// Now use the property of the current object
loadUsernameInfo(mentions[this.idx])
};
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", "");
}