swapping src to lazy - javascript

Not happy with existing lazy loading plugins (they are so complex that I can't even understand the codes in them), I thought I can create a simple, easy to understand script that lazy loads images.
My idea was simple. Start out with a following (deformed) img tag.
<img lazy="http://lorempixel.com/400/200"/>
Using javascript, swap out the word "lazy" with "src" when the page is being scrolled up or down.
function hackyaFunction_lazy() {
[].forEach.call(document.querySelectorAll('img[lazy]'), function(img) {
img.setAttribute('src', img.getAttribute('lazy'));
img.onload = function() {
var imgs = document.querySelectorAll('[lazy]');
for (i = 0; i < imgs.length; i++) {
var o = imgs[i],
lazy = o.getAttribute('lazy');
if (o.y <= (window.screen.height + window.scrollY + 50) && o.y >= (window.scrollY - 50)) {
o.setAttribute('src', lazy);
o.removeAttribute('lazy');
}
}
}
window.onload = window.onscroll = hackyaFunction_lazy;
});
}
I know enough of javascript to hack together bits & pieces to make something that works. And the above code kinda works.
On my console, I see that the word "lazy" has been successfully replaced with "src" for images that are in viewport.
However, images that are outside of the viewport, this is what I see.
<img lazy="http://lorempixel.com/400/200/sports/Dummy-Text" src="http://lorempixel.com/400/200/sports/Dummy-Text">
So I have half working code & this is the best that I can do.
I should have just used any one of the plugins that are available out there; just wanted to see if I can create something simple & easy.
Now that I am so close to making this thing work, I am reluctant to give it up.
Any help will be greatly appreciated.
I wanted to put together the whole thing (html & js) on jsfiddle but images fail to show on jsfiddle. Don't know why.
The code works (as described) on local environment.

The first line within the .forEach() changes the src of the current image without removing lazy and without testing whether it is in view or not, so after the .forEach() has run that line for every image they will all have been updated. That's why when you inspect the elements with the dev tools many of the images have both a src and lazy attribute.
If the idea is to only set the src once the image scrolls into view, and for any images that are already in view on page load, you can do it by keeping just the inner for loop of your function:
function hackyaFunction_lazy() {
var imgs = document.querySelectorAll('[lazy]');
for (i = 0; i < imgs.length; i++) {
var o = imgs[i];
if (o.y <= (window.screen.height + window.scrollY + 50) && o.y >= (window.scrollY - 50)) {
o.src = o.getAttribute('lazy');
o.removeAttribute('lazy');
}
}
}
window.onload = window.onscroll = hackyaFunction_lazy;
This works because on each scroll it first selects whatever elements still have the lazy attribute, then for those elements it tests whether they're in view and if so it sets their src and removes the lazy. Elements that have not yet been scrolled into view are not updated.
See it working here: https://jsfiddle.net/vb779g7v/2/
Edit/P. S. Note that your if test checking if the element is in view isn't right, because screen.height gives, as the name suggests, the height of the whole screen, not the height of the client area within the browser. But that's not really central to your question, so I'll leave fixing that as an exercise for the reader...

Related

Basic JS - Is this function okay?

I have a carousel (Owl Carousel) with vertically centered controls. Because of the structure, I have to absolutely position the previous and next arrow. Because the page is responsive, their position is dynamic. The size of the controls may also change.
I've written a function that runs on load and resize. It gets the height of the image and the height of the controls, subtracts the latter from the former, divides by two, and then uses that number as the controls' margin-top.
It works, but I'm questioning if I'm getting and using all the variables correctly. Does JavaScript read in order? Where it runs the first line, then the next, then the next... I'm strong in CSS but JS has always been a crutch.
Can I write this more efficiently?
function centerCarouselControls() {
var carouselImage = $('.carousel-card > img');
var carouselControls = $('.owl-nav > div');
var carouselHeight = carouselImage.outerHeight();
var controlHeight = carouselControls.outerHeight();
var controlMargin = (carouselHeight - controlHeight) / 2;
carouselControls.css('margin-top', controlMargin);
}
$('.carousel-card > img').load(centerCarouselControls);
$(window).on('resize', centerCarouselControls);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I feel like this might be the type of question that gets flagged on here for not being specific enough. If that's the case, could someone please point me to a community where this would be more appropriate? Thanks!
In some browsers your code works like firefox 51, but it is more complete code this:
carouselControls.css('margin-top', controlMargin + 'px');

Javascript too fast for DOM-manipulation?

In Javascript I load some elements via AJAX from a server, and for each data-element I create a <div> that contains another div and an <img>. The inner div contains between 2 and 5 lines of text. I arrange three of this items side by side in a row, then the next three in the next row and so on.
But since the texts have different lengths, I want to do some fine-adjustment of the elements within each row. I want the top-edges of all three images to be on the same height. To do this, I do this:
Insert the three <div><div>text</div><img></div>-blocks into their container.
Get the heights of the three <div>text</div>-elements,
calculate their maximum, and then
set their padding-top properties in a way that gives them all the same height.
In Safari and Chrome this works perfectly fine when I turn on the console and set breakpoints to watch what is going on in detail.
But when I turn off breakpoints in this two browsers, the text-diffs don't get their correct padding-values.
I am pretty sure, that - at breakpoints off - the browser is still working on inserting the three dom-elements and rendering the pictures, when javascript tries to measure the heights of the text-divs. So it measures them at a time when they don't have their final values. The script reads wrong values and so it puts wrong values to the padding-top-property.
This does not happen in all browsers:
When running normally (without console and breakpoints) it works always fine within:
Firefox
Opera
Internet Explorer (running in a virtual Machine on my iMac)
But I have those problems in:
Safari
Chrome
What can I do to ensure, that the measurement of an elements height happens when the rendering-machin has finished its manipulation?
EDIT:
I found out another very important detail:
The problem occurs, because of the height of the text-div above the image. Sometimes the text fits very tightly into two rows. One small letter more in any of the rows and it would be three rows.
In this case my script, that runs immediately after the div was created, measures a height of three lines (60 pixel), and everything would be absolutely correct, if this div did really contain 3 lines of text. My script manipulates the elements in a manner that would be perfect if this div really was 3 lines high.
But obviously, some milliseconds after my script was running, the browser (Safari and Chrome) performs an improvement of font-rendering. And then suddenly the text fits into 2 lines, which makes the text-div only 40 pixels high. And so the image moves up 20 pixels, and this destroys my just processed result (all images was at the same position)
So,does this give you any idea on how to solve the problem? Is there a way to let that part of my script run after all rendering-polishing has finished? Is there an event like onFinishingRenderingImprovementsDone?
(written on May the 4th be with you = Star Wars day)
How about having your script run with a short delay - setTimeout(function() {[your code here]}, 100) (or however long it needs...) - and see if you can simply avoid the problem altogether? Less than a second probably, and for an async action, adding a very short wait would likely not be noticeable.
You can monitor the height of the elements, and when an element height changes, you can recalculate the padding.
Example:
$.fn.changeHeight = function(callback){
return this.each(function(i, e){
var el = $(e), height = el.height();
window.setInterval(function(){
var h = el.height();
if (h != height) {
height = h;
callback.call(e);
}
}, 100);
});
};
function rndText() {
var txt = '';
for (var i = Math.floor(Math.random() * 20); i >= 0; i--) {
txt += 'asdf ';
}
return txt;
}
for (var i = 0; i < 10; i++) {
var d = $('<div>').append(
$('<div>').prop('contenteditable', true).text(rndText()).changeHeight(resize)
).appendTo('body');
}
resize();
function resize() {
console.log('resize');
var height = 0;
$('body > div').each(function(i, e) {
var d = $(e);
var h = d.find('div').height();
if (h > height) height = h;
});
$('body > div').each(function(i, e) {
var d = $(e);
d.css('padding-top', height - d.find('div').height());
});
}
Demo: http://jsfiddle.net/Guffa/Lunxbr6p/2/
The boxes in the demo are editable, so you can change the text and see how the boxes resize.

Setinterval Animation sometime paused

I make an animation using sequence images. i run the images using setinterval function animation going fine but i dont know why it paused some time. i posted fiddle here look this fiddle you can able to notice this pause
Unwanted Pause Happen Here
myAnim = setInterval(function(){
$("#myImageHolder8").attr('src', nextImage5[u]);
u++;
if(u==nextImage5.length)
{
u=0;
}
}, 50);
Pls friends Help me.
You need to preload the image. Setting the image source inside the loop will definately cause a hick-up at one point as loading and decoding the image(s) may very well exceed 50 ms (cached or not). This will also cause the problem to appear randomly (and faster computers may not notice while slower one or slower connections may cause this more frequent).
Preload the images and the simply insert the loaded image into the container (a parent element) instead.
You can preload the images either by hiding them in DOM and use window.onload to start, or do it in JavaScript using an array and load counter.
An example of an loader:
Live demo
var images = [],
count = nextImage5.length,
len = count,
i = 0;
for(; i < len; i++) {
var img = new Image;
images.push(img);
img.onload = loader;
img.src = nextImage5[i];
}
function loader() {
count--;
if (count === 0) {
... start animation here...
}
}
and then in the animation loop do something like (sorry, my jQuery escapes me but you see the point):
$('#myImageHolder8').html('');
$('#myImageHolder8').append(images[u]);
Before setting the animation, make sure you preload your images first. So that the images are ready and loaded to display smoothly. You can use this preloader. And I would like to suggest that instead of changing the src of an <img> it is better to draw the image to a <canvas> then create a <canvas> for your next image.

CSS Display affects jQuery/Javascript?

I have run into a strange phenomena I believe and I was wonder if anyone has a the answer to why this occurs. I have been doing a lot of manipulation of images for a photography site using a custom jQuery slideshow I created and have run into some problems.
I have a gallery here: http://www.daemondeveloper.com/photography/gallery.php
I have been adding some functions that resize the images in this gallery so that they scale to the size of the preview image size. As you can see, the very last image is panoramic and does not fill up the entire height of the div even though I have javascript telling it to resize.
If you refresh the page, the javascript seems to work all of a sudden and the pictures scales how it should.
Now try clicking on the panoramic picture of the girl and my slideshow will appear displaying the image scaled and centered vertically using jQuery. The function below is what handles clicking on the small image previews in the gallery.
If you look at where the commented changeSize() function is, that is where I USED to have the function and the scaling did not work. Then I moved it after the .show() functions which show my slideshow and now it works. So it appears that the display:none; affected how the javascript fired because when I debugged, the currentImg object was null, as if the .slides selector did not exist when it was set to display:none;. Is this really happening or am I just seeing a side effect of something else?
If this is really happening it may have something to do with the cause of the first problem I stated about the panoramic image not scaling on the first load of the gallery.php page.
$('.imgHolder').click(function(){
currentPosition = $('.imgHolder').index(this);
$('#slideShow').width(slideWidth);
// Remove scrollbar in JS
$('#slideContainer').css('overflow', 'hidden');
// Wrap all .slides with #slideInner div
slides.css({
'float' : 'left',
'width' : slideWidth
});
// Set #slideInner width equal to total width of all slides
$('#slideInner').css('width', (slideWidth * numberOfSlides));
// Hide left arrow control on first load
manageControls(currentPosition);
$('#slideInner').css('margin-left' , slideWidth*(-currentPosition));
//changeSize(); used to be here
$('#filter').show();
$('#photoWrap').show();
//Change image scale and center
changeSize();
});
And here is the changeSize() function that does the scaling and centering
function changeSize(){
currentSlide = $('.slide').get(currentPosition);
currentImg = $(currentSlide).children('img:first')[0];
imgRatio = $(currentImg).height() / $(currentImg).width();
if(imgRatio < slideRatio)
{
$(currentImg).addClass('landscape');
//Vertically align
var thisHeight = $(currentImg).height();
$(currentImg).css('margin-top', ($('#slideShow').height()/2)-(thisHeight/2));
}else{
$(currentImg).addClass('portrait');
}
}
$('#gallery ul li').each(function() {
var img = $(this).children('div').children('img').first();
var ratio = img.height() / img.width();
var goal = img.parent('div').height() / img.parent('div').width();
if (ratio < goal) {
img.addClass('portrait');
img.css('margin-left', -(img.width() / 2) + ($(this).children('div').width() / 2));
} else {
img.css('width', '100%');
}
});
Here I removed the unnecessary $() instances from your code, as you have already selected the element that you wish to call your methods on when you set the img variable. I doubt that this redundancy is the ultimate issue, but it is a good place to start.
Update your code to this and let's debug from there.
EDIT:
I think I found your error (well, I found one at least):
function configGallery()
{
var currentPosition;
var slides = $('.slide')
var currentSlide;
var currentImg;
var slideWidth = 720;
var numberOfSlides = slides.length;
...
}
Do you see what's wrong here? You forgot a semi-colon after var slides = $('.slide') and that could be your issue. Honestly, I'm surprised any of your scripts ran at all. Missing semi-colons usually crash the whole thing.
UPDATE:
Here are a few more selectors for you to remove the $() from when you get a chance:
function changeSize(){
currentSlide = $('.slide').get(currentPosition);
currentImg = $(currentSlide).children('img').first();
imgRatio = $(currentImg).height() / $(currentImg).width();
if(imgRatio < slideRatio)
{
$(currentImg).addClass('landscape');
//Vertically align
var thisHeight = $(currentImg).height();
$(currentImg).css('margin-top', ($('#slideShow').height()/2)-(thisHeight/2));
}else{
$(currentImg).addClass('portrait');
}
}
UPDATE:
Okay I wrote you a little fiddle to help you re-write your image-sizing function. I'll work on prettying it up and putting it in a plugin for you.
UPDATE:
Here's the same function again in a quick and dirty plugin: http://jsfiddle.net/Wj3RM/3/
I didn't pretty it up though - I figured it would be easier for you to adapt and modify like this.

Remove images from UIWebview

In my iPhone app, an epub reader, based off the method here, I have parsed the epub, created the UIWebviews, but I have a slight problem. There are images in the epubs that are larger than the width of the iPhone's screen (320 px.). Is there a Javascript method I can invoke on the UIWebview ([view stringByEvaluatingJavaScriptFromString:SomeJavaScriptString]) and remove those images programatically without changing the epub manually?
UPDATE: Could the issue be that the source file is an xml file and not HTML?
You probably want something like: document.getElementById('id_of_your_image').style.visibility = 'hidden'
UPDATE
To hide all images in a document,
for (i=0; i<document.getElementsByTagName("img").length; i++) {
document.getElementsByTagName("img")[i].style.visibility = 'hidden';
}
should do the trick.
var images = Array.prototype.slice.call(document.getElementsByTagName('IMG'), 0);
var imageCount = images.length;
for (var i = 0; i < imageCount; i++) {
var image = images[i];
image.parentNode.removeChild(image);
}
If you're not dealing with graphs or images where seeing the detail is important why not:
1) write some CSS that sets a max-width on images of 320px or 640px, depending on orientation.... see http://www.thecssninja.com/css/iphone-orientation-css for how to use different css for different orientations.
2) insert that CSS in each HTML file after any other styles or stylesheet links - inside a <style> element added just before the </body> would work.
?
That way images are still visibile but won't extend past a single page width.
As a next step you could wrap images with something like <a href='zoom://x'> where x is the image filename - then in your UIWebViewDelegate' "shouldStartLoadWithRequest" function check to see if request.URL.scheme == "zoom" - if it is you could push a new view containing image x in a zoomable container, like iBooks.

Categories