I've a javascript function setErrorImages(), which searches all img tags and and gives them an onerror function. This works great.
I also have a division content with dynamic content by calling de jquery .load function like this, which also works:
$("#content").load("http://example.com/otherpage.php");
But when the new content (otherpage.php) was loaded into the division, the setErrorImages() function doesn't work on the img'es within the new content.
I can call the setErrorImages() function on the otherpage.php, and everything works perfectly, but this way I have to do this on every otherpage, which are a lot.
Is there a way to send a javascript file or function with the jquery .load,
or maybe there is a way to re-execute the javascript function right after the jquery .load.
Kind regards,
Liontack
function setErrorImages(){
var images = document.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
images[i].onerror = function(){ setErrorImage(this); };
}
}
function setErrorImage(image){
image.onerror = null;
image.src = 'http://tanuri.eu/images/error.png';
}
$("#content").load("http://example.com/otherpage.php", setErrorImages);
.load() accepts a "complete" parameter:
A callback function that is executed when the request completes.
Firstly, I suggest using the this context instead of a parameter for your error handler:
function setErrorImage() {
this.onerror = null;
this.src = 'http://tanuri.eu/images/error.png';
}
Secondly, change setErrorImages to use this too:
function setErrorImages() {
var images = this.getElementsByTagName('img');
for (var i = 0, n = images.length; i < n; ++i) {
images[i].onerror = setErrorImage; // parameter is now implict
}
}
Note how there's now no need for a function wrapper for the onerror call - the browser automatically sets this to be the element of interest.
For simpler code you could also just use jQuery to do this job for you since you already have it loaded:
function setErrorImages() {
$('img', this).error(setErrorImage);
}
Thirdly, use the complete handler of .load, taking advantage of the fact that it sets this to be the parent of the part of the DOM that just got replaced. This avoids setting the onerror handler on elements in the page that you already looked at.
$('#content').load(url, setErrorImages);
The only other change is that on your initial invocation you'll need to pass document to setErrorImages using .call:
setErrorImages.call(document);
Related
How to show callback function's parameter to user?
I'm writing a pagination component for table by javascript.
And this is my component code. (not finished yet)
function ComponentPagination(paginationAreaID, pageViewDataCount ,
totalDataCount, ajaxUrl)
{
//div
var paginationArea = document.getElementById(paginationAreaID);
//prev button
var prevBtn = document.createElement("a");
prevBtn.textContent = "Prev";
paginationArea.appendChild(prevBtn);
//page buttons
var pageCount = (totalDataCount / pageviewDataCount);
for (var i = 0; i < pageCount; ++i) {
var pageBtn = document.createElement("a");
pageBtn.textContent = (i + 1) + "";
paginationArea.appendChild(pageBtn);
}
//next button
var nextBtn = document.createElement("a");
nextBtn.textContent = "Next"
paginationArea.appendChild(nextBtn);
//Use this method to set callback function
//The call back function called when user click page button <a> or
//next or prev button.
this.addCallBack = function(onUpdatePagingMethod)
{
// (example)
var clickedButton;// = clickedButtonIndex;
this.onUpdatePagingMethodCallBack = onUpdatePagingMethod;
//and then it will call when update will neccessay
//like this.onUpdatePagingMethodCallBack(clickedButton, ajaxUrl);
}
}
User will use this like..
<script>
windows.onload = function()
{
//the total data is 105 and the page will show each 10 items
var pagination = new ComponentPagination("pageArea", 10,
105,"/API/FileList");
//Register CallBack
pagination.addCallBack( onPageUpdate );
}
And then the user will design the callback function which name is on PageUpdate.
But, user can't know the callback function's parameter info which
addCallBack() method want. Like This.
function onPageUpdate(/* hum? how should i know the parameter? */)
{
}
well.. In c or c++ have function pointer(maybe they use typedef), so it can limit the parametes numberand each type and user can infer how to design callback function and parameters meaning.
I have no ideas how to limit or invoke parameter info to user in javascript.
Is there have any ideas about this? The comment is only way to solve this problem?
p.s : Not like ts, i want only js.
Javascript doesn't have any way to declare the signature of callback functions in code. You should put it in the documentation of your component, e.g.
onPageUpdate: function(string argName1, object argName2, ...)
For an example, see how jQuery describes the callback function in jQuery.each
Well if you want the parameters you can use arguments:
function onPageUpdate()
{
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
All function parameters are present in the Arguments Scope
you do console.log(arguments) inside any function and it'll log all the parameters on your console.
Edit:
predefining a signature of a callback isn't possible in JavaScript except via the usual way of documentation by a comment.
This is possible in TypeScript though, in a similar format.
public myCallback: (name: type) => returntype;
I am using modular pattern of javascript and trying to do things in Javascript way rather than Jquery
myapp.module1 = (function($){
"use strict";
var _config = {
backgroundImages : document.getElementsByClassName('img_paste'),
}
for(var i = 0;i < _config.backgroundImages.length; i++){
var imageElement = _config.backgroundImages[i];
imageElement.addEventListener('click',myapp.module2.addBackgroundImage(imageElement),false);
}
// $('.img_paste').click(function(){
// var img = this;
// console.log(this);
// console.log($(this));
// myapp.module2.addBackgroundImage(img);
// });
})(jQuery);
In the above code, the Jquery click function works but not the Javacript one.
When I tried to debug, I tried to console out the image in addBackgroundImage() function.
var addBackgroundImage = function(imageToBeAdded){
console.log(imageToBeAdded);//
_addImageToCanvas(imageToBeAdded);
}
The function seems to be executing even before onclick. Why is that happening?
First, the images elements appear to be empty in the console, then after some some the image elements are displayed in console.
Take a look at this simple code example:
function describeTheParameter(p) {
console.log("describeTheParameter invoked. p is of type " + typeof(p));
}
function stringFunction() {
return "Hello World!";
}
describeTheParameter(stringFunction());
describeTheParameter(stringFunction);
This results in
describeTheParameter invoked. p is of type string
describeTheParameter invoked. p is of type function
In the first call, we are calling stringFunction, and then passing the result to describeTheParameter.
In the second call, we are actually passing the function to describeTheParameter.
When you call addEventListener you must follow the pattern of the second call: pass the function without invoking it:
In the following line of code, you are invoking addBackgroundImage, and then passing the result (which will be undefined) to addEventListener.
imageElement.addEventListener('click',myapp.module2.addBackgroundImage(imageElement),false);
You need to pass a yet-to-be-called function into addEventListener.
The smallest step to make your code work is to employ a currying function:
function addImage(imageElement) {
return function() {
myapp.module2.addBackgroundImage(imageElement);
}
}
for(var i = 0;i < _config.backgroundImages.length; i++){
var imageElement = _config.backgroundImages[i];
imageElement.addEventListener('click', addImage(imageElement), false);
}
For much simpler code, make use of the this keyword. In this case, this will point to the element that's firing the event.
function imageClickHandler() {
var imageElement = this;
myapp.module2.addBackgroundImage(imageElement);
}
for(var i = 0;i < _config.backgroundImages.length; i++){
var imageElement = _config.backgroundImages[i];
imageElement.addEventListener('click', imageClickHandler, false);
}
The function seems to be executing even before onclick. Why is that happening?
Look at the statement you wrote:
myapp.module2.addBackgroundImage(imageElement)
You are calling the function and then passing its return value as the function argument.
You want something more along the lines of:
myapp.module2.addBackgroundImage.bind(myapp.module2, imageElement)
(or the function expression that you used in the commented out code)
I have read countless of answers of this issue and I came up with the following, but it doesn't work either.
function fitToParent(objsParent, tagName) {
var parent, imgs, imgsCant, a, loadImg;
//Select images
parent = document.getElementById(objsParent);
imgs = parent.getElementsByTagName(tagName);
imgsCant = imgs.length;
function scaleImgs(a) {
"use strict";
var w, h, ratioI, wP, hP, ratioP, imgsParent;
//Get image dimensions
w = imgs[a].naturalWidth;
h = imgs[a].naturalHeight;
ratioI = w / h;
//Get parent dimensions
imgsParent = imgs[a].parentNode;
wP = imgsParent.clientWidth;
hP = imgsParent.clientHeight;
ratioP = wP / hP;
//I left this as a test, all this returns 0 and false, and they shouldn't be
console.log(w);
console.log(h);
console.log(ratioI);
console.log(imgs[a].complete);
if (ratioP > ratioI) {
imgs[a].style.width = "100%";
} else {
imgs[a].style.height = "100%";
}
}
//Loop through images and resize them
var imgCache = [];
for (a = 0; a < imgsCant; a += 1) {
imgCache[a] = new Image();
imgCache[a].onload = function () {
scaleImgs(a);
//Another test, this returns empty, for some reason the function fires before aplying a src to imgCache
console.log(imgCache[a].src);
}(a);
imgCache[a].src = imgs[a].getAttribute('src');
}
}
fitToParent("noticias", "img");
To summarise, the problem is the event onload triggers before the images are loaded (or that is how I understand it).
Another things to add:
I don't know at first the dimensions of the parent nor the child,
because they varied depending of their position on the page.
I don't want to use jQuery.
I tried with another function, changing the onload event to
window, and it worked, but it takes a lot of time to resize because
it waits for everything to load, making the page appear slower,
that's how I came to the conclusion the problem has something to do
with the onload event.
EDIT:
I made a fiddle, easier to look at the problem this way
https://jsfiddle.net/whn5cycf/
for some reason the function fires before aplying a src to imgCache
Well, the reason is that you are calling the function immedeatly:
imgCache[a].onload = function () {
}(a);
// ^^^ calls the function
You call the function and assign undefined (the return value of that function) to .onload.
If you want to use an IIFE to capture the current value of a, you have to make it return a function and accept a parameter to which the current value of a is assigned to:
imgCache[a].onload = function (a) {
return function() {
scaleImgs(a);
};
}(a);
Have a look again at JavaScript closure inside loops – simple practical example .
I have an array of list items in a piece of Javascript code. I would like to assign an onclick event handler to each one. Each handler would be the same function, but with a different input argument. Right now I have:
function contentfill(i) {
box = document.getElementById("text");
box.style.background="rgba(0,0,0,0.8)";
var content = new Array();
contentdivs = document.querySelectorAll("#contentfill>div");
box.innerHTML = contentdivs[i].innerHTML;
}
li[3].onclick = function() {contentfill(0);};
li[4].onclick = function() {contentfill(1);};
li[5].onclick = function() {contentfill(2);};
This works well enough, but I would like to achieve the same thing with a loop, for example:
for(i=3;i<=5;i++) {
j=i-3;
li[i].onclick = function() {contentfill(j);};
}
This, however, does not work. Since j seems to be defined as 2 at the end of the loop, each time I click, it only seems to call contentfill(2).
For an alternative approach, consider having each of the elements aware of what argument it should be using.
for (var i = 0; i < 3; i++) {
var el = li[i + 3];
el.dataset.contentIndex = i;
el.addEventListener('click', contentfill);
}
Then contentfill would have to extract the argument from .dataset instead of taking an argument, of course. (This is the same mechanism as jQuery's $.data.)
I tend to prefer this since (a) it doesn't generate tons of tiny wrappers, (b) it allows me to later examine and possibly change the "arguments", and (c) it lets me predefine them in the document using data- attributes. Effectively changes them from function arguments into behavior.
The value of i - 3 should be bound to the click handler function; a closure can provide this functionality:
li[i].onclick = (function(j) {
return function() {
contentfill(j);
}
)(i - 3));
Btw, it's better practice to use addEventListener or attachEvent to register click handlers.
How can I do a spinlock in javascript?
I'm trying to load a bunch of images and I can only move forward after everything is loaded, so I have a spinlock like
for(...)
image[i].onload = function() { ++imagesloaded; }
while(imagesloaded != totalimages)
{
}
And it crashes my browser. Is there a better way to do it? Or a yield / sleep function I'm missing?
Short answer: don't spinlock.
Longer answer: here's how to do it:
var imagesLoaded = 0;
var totalImages = 42;
function handleImageLoad()
{
imagesLoaded++;
if (imagesLoaded === totalImages)
{
doSomething();
}
}
for (var i=0; i<totalImages; i++)
{
image[i].onload = handleImageLoad;
}
In general, when you want to sleep/wait/spin in JavaScript, instead think about solving the problem in terms of callbacks (and setTimeout/setInterval).
The answers above aren't useful as spinlocks may be required because of limitations/bugs in browsers. For instance safari (hopefully not future versions) requires the use of method window.open when you want to generate a file in javascript. The consequence of this is that you cannot generate the file using any callbacks (because of popup blockers), this in effect forces the use of a dialog window that first calls the file generation function (using callbacks) and then a button that downloads the file. Because spinlocks don't work the code becomes the following:
function process(callback) {
processCallbackData = null; // global var that must be a unique name
callback(function(data) {
processCallbackData = data;
});
}
function fowardButton() {
if(processCallbackData!=null) {
goForwardUsingCallbackIncompatibleCode();
} else {
displayStillLoadingWarning();
}
}
Don't use a loop to check. Check in the event handler function. (So you only do the check when an image has loaded, not continuously and as quickly as possible)