I am trying to create a bookable product, where upon selected (= selectbox) a room type, the picture changes to that specific room with good old javascript.
the interesting part is that it works for the first element of the HTML collection, but the last element is giving an undefined and makes it impossible to override.
I am not getting why that is. I tried via the console log to view what I am missing, but I see nothing problematic.
HTML collection:
0: <a href="https://staging.deloftli…09/Steck-coachruimte.jpg" hidefocus="true" style="outline: currentcolor none medium;">
1: <img class="zoomImg" role="presentation" alt="" src="https://staging.deloftli…09/Steck-coachruimte.jpg" style="position: absolute; top:…none; max-height: none;">
I have the following script:
<script id="bookingschanges">
var activities = document.getElementById("wc_bookings_field_resource");
var image = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[0].firstChild;
var zoompic = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[1];
activities.addEventListener("click", function() {
var options = activities.querySelectorAll("option");
});
activities.addEventListener("change", function() {
if(activities.value == "1949")
{
image.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
image.srcset = ""
zoompic.scr = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
}
console.log(image);
console.log(zoompic);
});</script>
The first element (image) is correct, the second element (zoompic) gives undefined.
To see it live, go to https://staging.deloftlisse.nl/product/vergaderruimte-huren/ and check the console log.
What am I missing here?
Variable zoompic is not defined at the time the variable is declared (its called before the element is created on loading, debug the page and refresh it to see) you will need to use an onload event listener.
https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
As someone else has suggested it would be better to call the image change function in the original javascript to change the image that is selected and you will avoid any issues. This might not be easy though if it is an external library.
EDIT: Added an example of onLoad
window.addEventListener('load', (event) => {
var activities = document.getElementById("wc_bookings_field_resource");
var image = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[0].firstChild;
var zoompic = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[1];
activities.addEventListener("click", function() {
var options = activities.querySelectorAll("option");
});
activities.addEventListener("change", function() {
if (activities.value == "1949") {
image.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
image.srcset = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg 768w";
zoompic.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
}
console.log(image);
console.log(zoompic);
})
});
Related
I have an array of URIs that represent .png elements, e.g., "./img/diamond-red-solid-1.png".
I want to assign each element of the array "gameDeck[0], gameDeck[1], etc. to div ids in HTML. Do I need to identify the elements as = SRC.IMG?
var gameDeck[];
var gameBoardCards = function () {
for (let cardArr of cardsToLoad)
gameDeck.push("./img/" + cardArr + ".png");
}
gameBoardCards();
document.addEventListener('DOM Content Loaded', function () {
gameDeck[0] = document.getElementById("card1");
gameDeck[1] = document.getElementById("card2");
etc.
});
The way I'm understanding your question is that you would like to target the divs in your HTML with ids of card1, card2, card3... card12 etc.
You would like to insert an img tag into each of these divs with the src being the URIs of the gameDeck array.
The following code achieves this. I've tested it and it works fine. Hope it helps :)
document.addEventListener('DOMContentLoaded', function () {
//iterate through the gameDeck array.
for (let x = 0;x < gameDeck.length;x++){
//create an img tag for each gameDeck element
var imgElement = document.createElement("img");
//set the source of the img tag to be the current gameDeck element (which will be a URI of a png file)
imgElement.src = gameDeck[x];
//target the div with id "card(x + 1)"
var cardID = "card" + (x + 1);
var cardElement = document.getElementById(cardID);
//append the img tag to the card element
cardElement.appendChild(imgElement);
}
//log the HTML to the console to check it
console.log(document.getElementById('body').innerHTML);
});
Here is a way that you can either insert images as background images, or as <img /> elements into the divs you are referring to:
<div id="card0" style="width: 100px; height: 100px;"></div>
<div id="card1" style="width: 100px; height: 100px;"></div>
let loadedImage = [];
function preloadImages(urls, allImagesLoadedCallback) {
let loadedCounter = 0;
let toBeLoadedNumber = urls.length;
urls.forEach(function(url) {
preloadImage(url, function() {
loadedCounter++;
console.log(`Number of loaded images: ${loadedCounter}`);
if (loadedCounter == toBeLoadedNumber) {
allImagesLoadedCallback();
}
});
});
function preloadImage(url, anImageLoadedCallback) {
img = new Image();
img.src = url;
img.onload = anImageLoadedCallback;
loadedImage.push(img);
}
}
function gameBoardCards() {
for (let i = 0; i < loadedImage.length; i++) {
document.getElementById(`card${i}`).style.backgroundImage = `url('${loadedImage[i].src}')`;
// document.getElementById(`card${i}`).appendChild(loadedImage[i]);
}
}
preloadImages([
`https://upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Color_icon_green.svg/2000px-Color_icon_green.svg.png`, `https://upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Solid_blue.svg/225px-Solid_blue.svg.png`
], function() {
console.log(`all images were loaded`);
gameBoardCards();
// continue your code
});
It may seem like a bit much for what you are trying to accomplish, but I threw in a proper image-loading handler there. The preloadImages function will handle the loading of images, that way they are properly preloaded, and can render to the DOM. Often times, we try to use images before they are properly loaded, resulting in them sometimes not being displayed, despite no errors are being thrown.
The rest of the code is straight forward, in the for loop, it loops through the existing divs and you can either use the current active line document.getElementById(`card${i}`).style.backgroundImage = `url('${loadedImage[i].src}')`; to use the loadedImage[i] image src to load that as the divs's background image. Or you can use the commented-out line directly below that document.getElementById(`card${i}`).appendChild(loadedImage[i]); to insert an <img /> element into that div. Just use either one that works for you.
You can see the code in action in this JS Fiddle demo.
Hope this helps :)
I am using Materialize CSS and have the "Material Box" which is a lightbox plugin. I want all of the thumbnails to be the same size. When clicked I want the full photo to load.
I am using onclick to change the src. How do I change it back to the thumbnail when the large photo closes (either with a click or the escape key)?
<div class="col s6 m3">
<img class="materialboxed responsive-img" src="images/thumb1.jpg" onclick='this.src="images/photo1"'>
</div>
Material Box Javascript
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.materialboxed');
var options = {}
var instances = M.Materialbox.init(elems, options);
});
// Or with jQuery
$(document).ready(function(){
$('.materialboxed').materialbox();
});
Materializecss.com - https://materializecss.com/media.html
I haven't found an easy other way of achieving the lightbox effect with cropped square thumbnails. Any advice would be greatly appreciated!
Here is one implementation of what you want, keeping track of the image click state.
$(document).ready(function(){
$('.materialboxed').materialbox();
// Image sources
const srcThumb = '/images/thumb1.jpg'
const srcPhoto = '/images/photo1.jpg'
// Click state
var clicked = false
// Get image element and bind click event
const img = $('.materialboxed')
img.on('click', function() {
img.attr('src', clicked ? srcPhoto : srcThumb)
clicked = !clicked
})
});
No need to rely on onclick in this case.
Materialize is already binding onclick for those images.
And it provides the following native methods we can use for doing exactly what you want using pure JS (no jQuery):
onOpenStart Function null Callback function called before materialbox is opened.
onCloseEnd Function null Callback function called after materialbox is closed.
In this example below, we assume there is a normal materialboxed photo gallery containing thumbnails named thumb_whatever.jpg, for example. But we're also serving the original sized photo named whatever.jpg in the same directory.
Then we're changing src attribute dynamically removing the thumb_ prefix to get the original image, which in this case will be imediately lightboxed by materialize.
And after closing the lightbox, the src attribute is being set back again without the thumb_ prefix.
We do that while initializing Materialbox:
// Initializing Materialbox
const mb = document.querySelectorAll('.materialboxed')
M.Materialbox.init(mb, {
onOpenStart: (el) => {
var src = el.getAttribute('src') // get the src
var path = src.substring(0,src.lastIndexOf('/')) // get the path from the src
var fileName = src.substring(src.lastIndexOf('/')).replace('thumb_','') // get the filename and removes 'thumb_' prefix
var newSrc = path+fileName // re-assemble without the 'thumb_' prefix
el.setAttribute('src', newSrc)
},
onCloseEnd: (el) => {
var src = el.getAttribute('src') // get the src
var path = src.substring(0,src.lastIndexOf('/')) // get the path from the src
var fileName = src.substring(src.lastIndexOf('/')).replace('/', '/thumb_') // get the filename and adds 'thumb_' prefix
var newSrc = path+fileName // re-assemble with the 'thumb_' prefix
el.setAttribute('src', newSrc)
}
})
This solution is also working like a charm for me, crossplatform.
This has gotten so far,that I will sum up what we found out:
Inside the event handler the attribute src cannot be read in IE8 (FF works fine), neither with jQuery nor with usual javascript
The only way to get the data was to get it outside the handler, write it to an array and read it afterwards from the inside of the handler
But there was still no possibility to write to src (neither jQuery nor javascript worked - only for IE 8)
I've got it working by writing the img elemts themselves to the document, but the reason behind this problem is no solved
The snippet we have is used twice.
The old code
<script type="text/javascript">
jQuery(document).ready(function() {
//...
//view entry
jQuery('.blogentry').live('click',function(){
// Get contents
blogtext = jQuery(this).children('.blogtext').html();
blogauthor = jQuery(this).children('.onlyblogauthor').html();
blogtitle = jQuery(this).children('.blogtitle').html();
profileimage = jQuery(this).children('.profileimage').html();
imgleft = jQuery(this).children('.Image_left').attr('src');
imgcenter = jQuery(this).children('.Image_center').attr('src');
imgright = jQuery(this).children('.Image_right').attr('src');
// Write contents
jQuery('#bild_left').attr('src', imgleft);
jQuery('#bild_center').attr('src', imgcenter);
jQuery('#bild_right').attr('src', imgright);
jQuery('.person').attr('src', profileimage);
jQuery('#g_fb_name').html(blogauthor);
jQuery('#g_titel').html(blogtitle);
jQuery('#g_text').html(blogtext);
//...
});
//...
// Change entry
jQuery('.blogentry').each(function(){
entryindex = jQuery(this).attr('rel');
if (entry == entryindex)
{
// The following works fine (so 'children' works fine):
blogtext = jQuery(this).children('.blogtext').html();
blogauthor = jQuery(this).children('.onlyblogauthor').html();
blogtitle = jQuery(this).children('.blogtitle').html();
profileimage = jQuery(this).children('.profileimage').html();
// This does not work - only in IE 8, works in Firefox
imgleft = jQuery(this).children('.Image_left').attr('src');
imgcenter = jQuery(this).children('.Image_center').attr('src');
imgright = jQuery(this).children('.Image_right').attr('src');
//alert: 'undefined'
alert(jQuery(this).children('.Image_center').attr('src'));
//...
}
}
//...
});
</script>
The new code
Please see my own posted answer for the new code.
UPDATE:
This does not work if called inside of the click event!!!
jQuery('.Image_left').each(function(){
alert(jQuery(this).attr('src'));
});
SOLUTION TO GET THE IMAGE DATA:
relcounter = 1;
imgleft_array = new Array();
jQuery('.Image_left').each(function(){
imgleft_array[relcounter] = jQuery(this).attr('src');
relcounter++;
});
relcounter = 1;
imgcenter_array = new Array();
jQuery('.Image_center').each(function(){
imgcenter_array[relcounter] = jQuery(this).attr('src');
relcounter++;
});
relcounter = 1;
imgright_array = new Array();
jQuery('.Image_right').each(function(){
imgright_array[relcounter] = jQuery(this).attr('src');
relcounter++;
});
//... inside the eventhandler (entryindex = 'rel' of blogentry):
imgleft = imgleft_array[entryindex];
imgcenter = imgcenter_array[entryindex];
imgright = imgright_array[entryindex];
This works because it is not called inside the event handler and the sources are saved beforehand
BUT! I still cannot write the data, which is my aim:
jQuery('#bild_left').attr('src', imgleft);
jQuery('#bild_center').attr('src', imgcenter);
jQuery('#bild_right').attr('src', imgright);
UPDATE!!!
This is just crazy, I tried to write the data via usual javascript. This also works in FF, but no in IE8. Here really is some serious problem witt the attribute src:
document.getElementById('bild_left').src = imgleft;
document.getElementById('bild_center').src = imgcenter;
document.getElementById('bild_right').src = imgright;
alert(document.getElementById('bild_left').src);
This works in FF, but not in IE8, the attribute src remains undefined after writing! This seems to be not a jQuery problem at all!
children looks for immediate child elements only where as find looks for all the elements within it until its last child element down the dom tree. If you are saying find is working that means the element you are looking is not its immediate children.
Try to alert this jQuery(this).children('#Image_center').length see what you get.
FYI. Even when any element is not found jQuery will return an emtpy object it will never be null. So alert an emtpy object will always give you [object Object]. You should alwasy check for the length property of the jQuery object.
Try this
alert(jQuery(this).find('#Image_center').length);//To check whether element is found or not.
Bing Bang Boom,
imgright = jQuery(".Image_right",this).attr('src');
And why don't you easily use one working?
alert(jQuery(this).children('#Image_center').attr('src'));
change children to find
alert(jQuery(this).find('#Image_center').attr('src'));
It is probably the easiest solution, and when it work, why wouldn't you use it?
the problem is not in the attr('src') but in something else. The following snippet works in IE8:
<img id="xxx" src="yrdd">
<script type="text/javascript">
alert($('#xxx').attr('src'));
</script>
But if you for example change the the text/javascript to application/javascript - this code will work in FF but will not work in IE8
This has gotten so far,that I will sum up what we found out:
Inside the event handler the attribute src cannot be read in IE8 (FF works fine), neither with jQuery nor with usual javascript
The only way to get the data was to get it outside the handler, write it to an array and read it afterwards from the inside of the handler
But there was still no possibility to write to src (neither jQuery nor javascript worked - only for IE 8)
I've got it working by writing the img elemts themselves to the document, but the reason behind this problem is no solved
The new code
relcounter = 1;
imgleft_array = new Array();
jQuery('.Image_left').each(function(){
imgleft_array[relcounter] = jQuery(this).attr('src');
relcounter++;
});
relcounter = 1;
imgcenter_array = new Array();
jQuery('.Image_center').each(function(){
imgcenter_array[relcounter] = jQuery(this).attr('src');
relcounter++;
});
relcounter = 1;
imgright_array = new Array();
jQuery('.Image_right').each(function(){
imgright_array[relcounter] = jQuery(this).attr('src');
relcounter++;
});
//view entry
jQuery('.blogentry').live('click',function(){
// Get contents
entryindex = jQuery(this).attr('rel');
blogtext = jQuery(this).children('.blogtext').html();
blogauthor = jQuery(this).children('.onlyblogauthor').html();
blogtitle = jQuery(this).children('.blogtitle').html();
profileimage = jQuery(this).children('.profileimage').html();
imgleft = imgleft_array[entryindex];
imgcenter = imgcenter_array[entryindex];
imgright = imgright_array[entryindex];
// Write contents
jQuery('#entryimages').html('');
jQuery('#entryimages').html('<img class="rotate" width="132" height="138" id="bild_left" src="'+imgleft+'" /><img class="rotateright" width="154" height="162" id="bild_center" src="'+imgcenter+'" /><img class="rotate" width="132" height="138" id="bild_right" src="'+imgright+'" />');
jQuery('.person').attr('src', profileimage);
jQuery('#g_fb_name').html(blogauthor);
jQuery('#g_titel').html(blogtitle);
jQuery('#g_text').html(blogtext);
});
So I am just not using .attr('src') in the event handler....
Try to make a delay:
jQuery(document).ready(function() {
setTimeout(function () {
jQuery('.blogentry').each(function(){
// your code...
});
}, 100); // if doesn't work, try to set a higher value
});
UPDATE
Hope, this code will work.
$('.blogentry img').each(function(){
alert( $(this).attr('src') );
});
UPDATE
I'm not sure, but maybe IE can't read classes with uppercase first letter...
Try to change ".Image_center" to ".image_center"
UPDATE
Check your code again. You definitely have some error. Try this jsfiddle in IE8, attr('src') is showed correctly. http://jsfiddle.net/qzFU8/
$(document).ready(function () {
$("#imgReload").click(function () {
$('#<%=imgCaptcha.ClientID %>').removeAttr("src");
$('#<%=imgCaptcha.ClientID %>').attr("src", "Captcha.ashx");
});
});
I've been working on a flowchart type program that changes various parts of the html each time a new question is posed (this question is represented by the 'state' variable below), things like Title, description and images.
Currently, my HTML for the images is:
<a id="imageLink" target="_blank" href=""><img class= 'right' id= 'imageBox' style ='max-height:50%;' src='' /></a>
<h3 id ='imageBoxText' ></h3>
and the image link and the legend for the image are found in imageArray which looks something like this:
imageArray = {'questionOne': ['www.link to image.com,'Legend for image']}
In this case 'questionOne' would be 'state'.
So far, I can load a single image asynchronously using:
document.getElementById("imageBox").src = "http://www.ktcagency.com/img/loader.gif"; // load spinner
var img = new Image(); // load image asynchronously
var newsrc = imageArray[state][0];
img.onload = function () { // onload handler
document.getElementById("imageBox").src = newsrc;
};
img.src = newsrc;
I also add the description and link to the image while I'm at it:
document.getElementById("imageLink").setAttribute('href',imageArray[state][0])
document.getElementById('imageBoxText').innerHTML = imageArray[state][1];
OK, so now what I need to do is write a function that can do this for multiple images, side by side, reading from a modified imageArray that looks like:
imageArray = {'questionOne': [['www.linktoimage1.com,'Legend for image1'],['www.linktoimage2.com,'Legend for image2']]}
The code should be able to handle up to three images, but I certainly wouldn't mind if it could do any more.
I tried to do it using a table to represent the images and used a for loop with the code from earlier. The image would never load, stuck on the spinner for ever and the images would all bunch up on one side of the screen.
Any help appreciated, thanks very much.
Well, I think I see what you're trying to do.
It sounds like you're trying to make a slideshow of some kind.
This is an example of how I would have done it.
Here's the jsfiddle so you can see what's going on. http://jsfiddle.net/VodkaTonic/4KWLr/
(function () {
var imageArray = {
link: [['http://i.imgur.com/7OfqRbF.jpg', 'http://i.imgur.com/AHbc6zO.jpg','http://i.imgur.com/gW144OQ.jpg'],['http://i.imgur.com/v0V8hrB.jpg', 'http://i.imgur.com/9l40vIT.png','http://i.imgur.com/ZXYUttz.png']],
label: [['Legend for image1','Legend for image2', 'Legend for image3'],['Legend for image4','Legend for image5', 'Legend for image6']]
},
doc = document,
question = 0,
img = doc.getElementsByClassName('images'),
links = doc.getElementsByClassName('links'),
label = doc.getElementsByClassName('label'),
button = doc.getElementById('button');
function add (state) {
var arr = imageArray.link[state],
arr2 = imageArray.label[state];
arr.forEach(function(value,index,array) {
img[index]['src'] = value;
links[index]['href'] = value;
});
arr2.forEach(function(value,index,array) {
label[index]['innerHTML'] = value;
});
question++;
}
window.addEventListener('load', add(question), false);
button.addEventListener('click', function () {
add(question);
}, false)
}());
I have this script which should show the text "Loading..." while images are loading, then change the text to "loaded" when all images are loaded. I added a button to load new images to make sure that it works for dynamically loaded images as well.
This works perfectly in Chrome but in Firefox the "Loading..." text never appears. I have no idea why this would be. The page begins loading and not all images are loaded so it should create the text "Loading.." but it doesn't. Then when all images are done loading the text "Loading" appears.
I just don't get why one message would appear and the other wouldn't. Especially because there are no qualifications that have to be met before creating the "Loading..." text, it should just fire automatically.
jsfiddle Example | Full Page Example
$(document).ready(function() {
var checkComplete = function() {
if($('img').filter(function() {return $('img').prop('complete');}).length == $('img').length) {
$('.status').text('Loaded');
} else {
$('.status').text('Loading...');
}
};
$('img').on('load',function() {
checkComplete();
});
$('#button').click(function() {
$('img.a').attr('src' , 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg');
$('img.b').attr( 'src' , 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg');
checkComplete();
});
checkComplete();
});
You have several issues in the code.
First off, the checkComplete() function is not written correctly. It should be this:
var checkComplete = function() {
var imgs = $('img');
if(imgs.filter(function() {return this.complete;}).length == imgs.length) {
$('.status').text('Loaded');
} else {
$('.status').text('Loading...');
}
};
The main fix here is that the filter callback needs to refer to this.complete, not to $('img').prop('complete') because you are trying to filter a single item at a time.
Second off, you are relying on both .complete and .load working correctly AFTER you've changed the .src value. This is explicitly one of the cases where they do not work properly in all browsers.
The bulletproof way to work around this is to create a new image object for the new images, set the onload handler before you set the .src value and when both onload handlers have fired, you will know that both new images are loaded and you can replace the once you have in the DOM with the new ones.
Here is a version that works in FF:
$(document).ready(function() {
$('#button').click(function() {
var imgA = new Image();
var imgB = new Image();
imgA.className = "a";
imgB.className = "b";
var loaded = 0;
imgA.onload = imgB.onload = function() {
++loaded;
if (loaded == 2) {
$("img.a").replaceWith(imgA);
$("img.b").replaceWith(imgB);
$('.status').text('Loaded');
}
}
// the part with adding now to the end of the URL here is just for testing purposes to break the cache
// remove that part for deployment
var now = new Date().getTime();
imgA.src = 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg?' + now;
imgB.src = 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg?' + now;
$('.status').text('Loading...');
});
});
Working demo: http://jsfiddle.net/jfriend00/yy7GX/
If you want to preserve the original objects, you can use the newly created objects only for preloading the new images and then change .src after they've been preloaded like this:
$(document).ready(function() {
$('#button').click(function() {
var imgA = new Image();
var imgB = new Image();
var loaded = 0;
imgA.onload = imgB.onload = function() {
++loaded;
if (loaded == 2) {
$("img.a")[0].src = imgA.src;
$("img.b")[0].src = imgB.src;
$('.status').text('Loaded');
}
}
// the part with adding now to the end of the URL here is just for testing purposes to break the cache
// remove that part for deployment
var now = new Date().getTime();
imgA.src = 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg?' + now;
imgB.src = 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg?' + now;
$('.status').text('Loading...');
});
});
Working demo of this version: http://jsfiddle.net/jfriend00/ChSQ5/
From the jQuery API .load method
Caveats of the load event when used with images
A common challenge developers attempt to solve using the `.load()` shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache