JQuery checking when element is in Viewport - javascript

I've tried a dozen libraries to achieve this effect but I must be making a mistake somewhere. I'm currently using this library:- zeusdeux/isInViewport.
In my css I have a style called .animated-element like so:-
.animation-element {
}
.in-view {
}
and I have several css elements that start an animation when the .in-view class is added:-
.trans-1 {
-webkit-transition: 2s;
transition: 2s;
}
.fade {
opacity: 0;
}
.fade.in-view {
opacity: 1;
}
In my html I have div elements with these classes for animating:-
<div class="fade trans-1 animation-element">
<h2 class="medium-subheader-bold">Header</h2>
</div>
Finally, the javascript checks whether an element is in the viewport using the library, and adds a class to the element if it is. Thereby changing the .fade to .fade.in-view (opacity 0 to 1):-
(function($) {
$(document).ready(function() {
$(window).scroll(function() {
$('.animation-element').removeClass('in-view');
$('.animation-element:in-viewport').addClass('in-view');
});
});
}(jQuery));
When I load the page in a browser, all the elements in the page are loaded immediately and animated. When I inspect the webpage to check elements that are not visible in the viewport, I can see that they are rapidly flickering between adding and removing the in-view class. I've also tried removing the $('.animation-element').removeClass('in-view'); line from the above javascript but the in-view class is still added immediately, even though elements are far out of the viewport.
I've tried this with several libraries and keep running into the same problem, so it must be the way I'm implementing it and not the fault of the library.
I should also say I'm using Pug to generate the html, regular css, and serving whole pages through node.js.

I finally figured out what the issue was. I spent a whole day trying to recreate it in JSFiddle and modifying different parts. In the end it was because I didn't put <!DOCTYPE html> at the top of the html document. Its crazy that everything else worked, even the animations when scrolling up, just not scrolling down, because of that one line.

Related

Lazyload and Masonry

I'm trying to get this layout working the way I want for WordPress blog. I can't figure out why Lazyload is causing the entire masonry to overlap and load in the same spot instead of a grid. When I remove the Lazyload, the layout functions as a masonry grid, along with the overlay hover.
The second issue is that this code:
.fade {
animation-name: fade;
animation-duration: 2s;
}
#keyframes fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
}
I can't figure out why I can't remove the unused bracket at the bottom. When I remove it, the entire masonry does not work, it becomes a complete broken mess.
Can anyone enlighten these two problems? Thank you!
Additionally, if you resize the window or flip your mobile device to the side and back, the masonry grid displays fine.
Update: So I found out WordPress 5.5, releasing in August, is including LazyLoad! Will reimplement LazyLoad again by that time.
For your lazyload and mansory problem, it might be a possible solution for you to "fake" or "simulate" the resize of the window. The grid works fine after resize, so it can be a way to trigger that event to make your code work.
You can use pure javascript, in wordpress you can use the shorter jQuery version:
Using pure Javascript:
var simulateResize = new Event('resize');
window.dispatchEvent(simulateResize );
Using jQuery:
$(window).trigger('resize');
Make sure you put the code at the bottom of the page, especially you have to put it after the code where the grid elements and contents have been generated.
For your CSS problem, the correct way to use the #keyframes rule is:
#keyframes mymove {
from {top: 0px;}
to {top: 200px;}
}
https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp
So your code is correct and the last } is unnecessary. If removing it causes your code to crash, there might be some #media rule around your .fade code, like:
#media only screen and (max-width: 600px) {}
which causes the problem if it opens up but do not close.

CSS Transition fails on jQuery .load callback

I have a function that loads html into a table with jQuery and subsequently adds a class to one of the rows with the callback. The function is triggered by various UI driven events on the page. I also have a css transition rule so the color should fade in (transition: background-color 1000ms linear). The function looks like this:
function load_tbody(row_id) {
$('tbody').load("load_tbody.php", function() {
$(row_id).addClass('green');
});
}
Once the html is loaded, the class successfully gets added and row color is set to green. However, my css transition rule seems to be ignored.
When I add a slight delay, even 10ms, it works fine:
function load_tbody(row_id) {
$('tbody').load("load_tbody.php", function() {
setTimeout(function() {
$(row_id).addClass('green');
}, 10);
});
}
The jQuery docs for .load() state:
If a "complete" callback is provided, it is executed after
post-processing and HTML insertion has been performed.
To me this would indicate the new elements have been loaded into the dom with existing styles applied and are ready for manipulation. Why does the transition fail in the first example but succeed in the second?
Here is a fully functional example page to demonstrate the behaviour in question:
http://so-37035335.dev.zuma-design.com/
While the example above links jQuery version 2.2.3 from cdn, actual page in question uses version 1.7.1. The same behavior can be observed across both versions.
UPDATE:
After considering some of the comments and answers offered below, I've stumbled upon something altogether more confusing. User #gdyrrahitis made a suggestion which lead me to do this:
function tbody_fade(row_id) {
$('tbody').load("load_tbody.php", function() {
$('tbody').fadeIn(0, function() {
$(this).find(row_id).addClass('green');
});
});
}
Adding the class inside the fadeIn() callback works, even with a duration of 0ms. So this had me wondering... if the element is theoretically there anyway, what background color does the browser think it has before I add that class. So I log the background-color:
console.log($(row_id).css('background-color'));
And do you know what? Simply getting the background-color color made everything work:
function tbody_get_style(row_id) {
$('tbody').load("load_tbody.php", function() {
$(row_id).css('background-color');
$(row_id).addClass('green');
});
}
Just adding the line $(row_id).css('background-color'); which seemingly does nothing at all causes the transition effect to work. Here's a demo:
http://so-37035335-b.dev.zuma-design.com/
I'm just dumbfounded by this. Why does this work? Is it merely adding a small delay or does jQuery getting the css property somehow have a substantial effect on the state of the newly added element?
jQuery load is intended to drop everything that is requested into the page.
You can leverage the power of jQuery Deferred objects by using $.get instead.
Take a look at this plunk.
Code snippet from plunk
function load_tbody(row_id) {
$.when($.get("load_tbody.html", function(response) {
$('tbody').hide().html(response).fadeIn(100, function() {
$(this).find(row_id).addClass('green');
});
}));
}
I am using the $.when which will run its callback as soon the $.get is resolved, meaning will fetch the HTML response. After the response is fetched, it is appended to the tbody, which is fadedIn (fadeIn method) and after it is shown, the .green class is added to the desired row.
Note that if you go and just append the html and then the class to the row_id, you won't be able to see the transition, because it is executed immediately. A little nice visual trick with the fadeIn can do the work.
Update
On newly added elements to the DOM, CSS3 transition is not going to be triggered. This mainly happens because of the internal browser engine that controls all animations. There are numerous articles with workarounds on the issue, as well as stackoverflow answers. Additional resources can be found there, which I believe can explain the topic much better than me.
This answer is about taking a step back and changing the piece of functionality that renders dynamic elements in DOM, without going to use setTimeout or requestAnimationFrame. This is just another way to achieve what you want to achieve, in clear and consistent way, as jQuery works across browsers. The fadeIn(100, ... is what is needed to catch up with the next available frame the browser is about to render. It could be much less, the value is just to satisfy visual aesthetics.
Another workaround is to not use transitions at all and use animation instead. But from my tests this fails in IE Edge, works well on Chrome, Firefox.
Please look at the following resources:
https://www.christianheilmann.com/2015/08/30/quicky-fading-in-a-newly-created-element-using-css/
Update 2
Take a look at the specification please, as interesting stuff lies there regarding CSS3 transitions.
...This processing of a set of simultaneous style changes is called a style change event. (Implementations typically have a style change event to correspond with their desired screen refresh rate, and when up-to-date computed style or layout information is needed for a script API that depends on it.)
Since this specification does not define when a style change event occurs, and thus what changes to computed values are considered simultaneous, authors should be aware that changing any of the transition properties a small amount of time after making a change that might transition can result in behavior that varies between implementations, since the changes might be considered simultaneous in some implementations but not others.
When a style change event occurs, implementations must start transitions based on the computed values that changed in that event. If an element is not in the document during that style change even or was not in the document during the previous style change event, then transitions are not started for that element in that style change event.
When element is added, reflow is needed. The same applies to adding the class. However when you do both in single javascript round, browser takes its chance to optimize out the first one. In that case, there is only single (initial and final at the same time) style value, so no transition is going to happen.
The setTimeout trick works, because it delays the class addition to another javascript round, so there are two values present to the rendering engine, that needs to be calculated, as there is point in time, when the first one is presented to the user.
There is another exception of the batching rule. Browser need to calculate the immediate value, if you are trying to access it. One of these values is offsetWidth. When you are accessing it, the reflow is triggered. Another one is done separately during the actual display. Again, we have two different style values, so we can interpolate them in time.
This is really one of very few occasion, when this behaviour is desirable. Most of the time accessing the reflow-causing properties in between DOM modifications can cause serious slowdown.
The preferred solution may vary from person to person, but for me, the access of offsetWidth (or getComputedStyle()) is the best. There are cases, when setTimeout is fired without styles recalculation in between. This is rare case, mostly on loaded sites, but it happens. Then you won't get your animation. By accessing any calculated style, you are forcing the browser to actually calculate it
Trigger CSS transition on appended element
Explanation For the last part
The .css() method is a convenient way to get a style property from the first matched element, especially in light of the different ways browsers access most of those properties (the getComputedStyle() method in standards-based browsers versus the currentStyle and runtimeStyle properties in Internet Explorer) and the different terms browsers use for certain properties.
In a way .css() is jquery equivalent of javascript function getComputedStyle() which explains why adding the css property before adding class made everything work
Jquery .css() documentation
// Does not animate
var $a = $('<div>')
.addClass('box a')
.appendTo('#wrapper');
$a.css('opacity');
$a.addClass('in');
// Check it's not just jQuery
// does not animate
var e = document.createElement('div');
e.className = 'box e';
document.getElementById('wrapper').appendChild(e);
window.getComputedStyle(e).opacity;
e.className += ' in';
.box {
opacity: 0;
-webkit-transition: all 2s;
-moz-transition: all 2s;
transition: all 2s;
background-color: red;
height: 100px;
width: 100px;
margin: 10px;
}
.box.in {
opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="wrapper"></div>
Here is the listed work arounds [SO Question]
css transitions on new elements [SO Question]
The main solution is understanding setTimeout(fn, 0) and its usages.
This is not related to CSS animations or jQuery load method.
This is a situation when you do multiple tasks into DOM.
And delay time is not important at all, the main concept is using setTimeout.
Useful answers and tutorials:
Why is setTimeout(fn, 0) sometimes useful? (stackoverflow answer)
Events and timing in-depth (real-life examples of setTimeout(fn, 0))
How JavaScript Timers Work (by jQuery Creator, John Resig)
function insertNoDelay() {
$('<tr><td>No Delay</td></tr>')
.appendTo('tbody')
.addClass('green');
}
function insertWithDelay() {
var $elem = $('<tr><td>With Delay</td></tr>')
.appendTo('tbody');
setTimeout(function () {
$elem.addClass('green');
}, 0);
}
tr { transition: background-color 1000ms linear; }
.green { background: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr><td>Hello World</td></tr>
</tbody>
</table>
<button onclick="insertNoDelay()">No Delay</button>
<button onclick="insertWithDelay()">With Delay</button>
This is a common problem caused by browsers. Basically, when new element is inserted it is not inserted immediately. So when you add the class, it is still not in the DOM and how it will be rendered is calculated after the class is added. When the element is added to the DOM, it already has the green background, it never had a white background so there is no transition to do. There are workarounds to overcome this as suggested here and there. I suggest you use requestAnimationFrame like this:
function load_tbody(row_id) {
$('tbody').load("load_tbody.php", function() {
requestAnimationFrame(function() {
$(row_id).addClass('green');
});
});
}
Edit:
Seems like the above solution doesn't work for all cases. I found an interesting hack here which triggers an event when an element is really parsed and added to the DOM. If you change the background color after the element is really added, the problem will not occur. Fiddle
Edit: If you want to try this solution (Won't work below IE9):
Include this CSS:
#keyframes nodeInserted {
from {
outline-color: #fff;
}
to {
outline-color: #000;
}
}
#-moz-keyframes nodeInserted {
from {
outline-color: #fff;
}
to {
outline-color: #000;
}
}
#-webkit-keyframes nodeInserted {
from {
outline-color: #fff;
}
to {
outline-color: #000;
}
}
#-ms-keyframes nodeInserted {
from {
outline-color: #fff;
}
to {
outline-color: #000;
}
}
#-o-keyframes nodeInserted {
from {
outline-color: #fff;
}
to {
outline-color: #000;
}
}
.nodeInsertedTarget {
animation-duration: 0.01s;
-o-animation-duration: 0.01s;
-ms-animation-duration: 0.01s;
-moz-animation-duration: 0.01s;
-webkit-animation-duration: 0.01s;
animation-name: nodeInserted;
-o-animation-name: nodeInserted;
-ms-animation-name: nodeInserted;
-moz-animation-name: nodeInserted;
-webkit-animation-name: nodeInserted;
}
And use this javascript:
nodeInsertedEvent= function(event){
event = event||window.event;
if (event.animationName == 'nodeInserted'){
var target = $(event.target);
target.addClass("green");
}
}
document.addEventListener('animationstart', nodeInsertedEvent, false);
document.addEventListener('MSAnimationStart', nodeInsertedEvent, false);
document.addEventListener('webkitAnimationStart', nodeInsertedEvent, false);
function load_tbody(row_id) {
$('tbody').load("load_tbody.php", function() {
$(row_id).addClass('nodeInsertedTarget');
});
}
Can be made into a generic solution or library. This is just a fast solution.

Dynamically repeat/tile div and child(s)?

I've been doing some searching on here and Google and I can't seem to find an answer that quite fits what I'm trying to do. I have a single div with some text in it that does a fade effect by transitioning to a different background image on mouse hover. What I want to do is to tile/repeat that same div dynamically so it fills the entire body (or parent div). Kind of like using background-repeat:repeat but with a div instead of a background image. I like to see what kind of cool visual effects I can achieve with elements across the entire page fading in and out as the mouse moves over them.
Of course I could just copy and paste the same div in the code a bunch of times but there must be a better solution. I'm thinking javascript is needed, but the only things I've been able to find about cloning divs look to be a bit over my head and I'm wondering if there is a more simple solution.
The CSS and HTML that I'm using as an example is from menu links on a site I'm working on. It may not be the best example but I'm a bit new to CSS. Basically I want to tile the below div across an entire page.
Here is the css:
#fadediv {
background-image:url(images/buttonback.png);
transition: background-image 0.5s linear;
-moz-transition: background-image 0.5s linear;
-webkit-transition: background-image 0.5s linear;
}
#fadediv:hover {
background-image:url(images/buttonback2.jpg);
}
.fadedivtext {
display:block;
width:320px;
height:138px;
float:left;
font-size:30px;
color:#FFF;
text-align:center;
line-height:138px;
}
And the HTML snippet:
<div id="fadediv" class="fadedivtext">about me</div>
EDIT: Looks like there's a PHP example here that could work, in addition to the javascript example given below.
I think clone should work well for you -- it's not that complicated, especially when you're talking about a basic div. Just make sure to target classes instead of IDs (you're not supposed to have multiple elements with the same ID).
Here's a basic example using JQuery's clone:
var numberOfClones = 20;
var el = $("#fadediv");
for (i=0 ; i<numberOfClones ; i++) {
var newEl = el.clone();
$("#container").append(newEl);
}​
http://jsfiddle.net/9P7bY/2/
Edit
This is a comment left by aug:
Or if you want to for some reason give each clone a unique id you can access the attribute field and change the id to something else
newE1.attr("id", newId);

Hide page until everything is loaded Advanced

I have a webpage which heavily makes use of jQuery.
My goal is to only show the page when everything is ready.
With that I want to avoid showing the annoying page rendering to the user.
I tried this so far (#body_holder is a wrapper inside body):
$(function(){
$('#body_holder').hide();
});
$(window).load(function() {
$("#body_holder").show();
});
This works completely fine, but messes up the layout.
The problem is that hiding the wrapper interferes with the other jQuery functions and plugins used (eg layout-plugin).
So I guess there must be another trick to do this. Maybe lay a picture or div over the body until window.load has occurred?
What approaches do you use?
EDIT:
The solution most likely has to be another way than display:none or hide();
Anything done with jQuery will normally have to wait for document.ready, which is too late IMHO.
Put a div on top, like so:
<div id="cover"></div>
set some styles:
#cover {position: fixed; height: 100%; width: 100%; top:0; left: 0; background: #000; z-index:9999;}
and hide it with JS when all elements are loaded:
$(window).on('load', function() {
$("#cover").hide();
});
Or if for some reason your script uses even longer time then the DOM elements to load, set an interval to check the type of some function that loads the slowest, and remove the cover when all functions are defined!
$(window).on('load', function() {
$("#cover").fadeOut(200);
});
//stackoverflow does not fire the window onload properly, substituted with fake load
function newW()
{
$(window).load();
}
setTimeout(newW, 1000);
#cover {position: fixed; height: 100%; width: 100%; top:0; left: 0; background: #000; z-index:9999;
font-size: 60px; text-align: center; padding-top: 200px; color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul>
<li>This</li>
<li>is</li>
<li>a</li>
<li>simple</li>
<li>test</li>
<li>of</li>
<li>a</li>
<li>cover</li>
</ul>
<div id="cover">LOADING</div>
Here is a jQuery solution for those looking:
Hide the body with css then show it after the page is loaded:
CSS:
html { visibility:hidden; }
JavaScript
$(document).ready(function() {
document.getElementsByTagName("html")[0].style.visibility = "visible";
});
The page will go from blank to showing all content when the page is loaded, no flash of content, no watching images load etc.
You should try setting visibility to hidden instead of display:none. Setting visibility to hidden will retain all elements positions and dimensions, thus it shouldn't create layout problems.
Start your HTML with:
<body style="opacity:0;">
At the end of your script:
document.body.style.opacity = 1;
Stumbled upon this and tried #9ete's solution but it didn't help me.
This worked instead:
CSS:
html { visibility:hidden; }
JS:
window.addEventListener('load', function () {
document.getElementsByTagName("html")[0].style.visibility = "visible";
});
As per documentation for window, the load event is fired after all the content (images included) is loaded while $document says that ready is fired after only the DOM is ready.
Your question is valid, but I would not get in a practice of hiding or covering the page while things are spinning up.
It keeps the user from understanding what's happening on the page. While lots of things may need to load, different parts of the page should spring to life as they're loaded. You should get in the practice of locking controls that are not ready, perhaps displaying a spinner or some other progress indicator over them. Or setting the cursor to wait on loading items.
This keeps the user in the loop and allows him to see and interact with parts as they come online instead of obscuring all parts until everything is ready.
You will normally want to load the things the user needs the quickest access to, usually stuff above the fold, first. Loading is a prioritization that can easily be coordinated with Promises.
At the very least seeing the page allows the user to get his bearings and decide what to do. Be transparent.
I was seeking a non-javascript solution so I found one that is working on most browsers in acceptable manner.
Since the loading order of CSS rules matters;
Define the hiding class in the first CSS file or inline in head.
.hidden-onpage-load{ display: none; }
In the body, the class can be used as
<div class="hidden-onpage-load"> ... </div>
Redefine it inline or in a CSS file after all other CSS and JS files are loaded
.hidden-onpage-load{ display: block; }
The simplest solution I've come up with is to wrap the body in a as suggested previously, but set it as hidden from the get go, then use JQuery (or javascript) to unhide on load after all components are loaded.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="bodyDiv" hidden>
Hello World!
</div>
<script>
$(document).ready(function() {
// add JQuery widget loads here
$("#bodyDiv").show(); // reveal complete page
})
</script>
Don't forget, a lot of frameworks use javascript to structure a page. To prevent the page from showing before these modification have been made you'll need to do something like what is described here (e.g. run a script at the end of the page to show the real contents of the page):
Detect if any JavaScript function is running
If you have a div #bodyholder then you can put display:none in your CSS for it and then with jQuery do:
$(document).ready(function() {
$('#body_holder').show();
});
I don't see why hiding a div should interfere with the loading of anything, because all it means is it is hidden. However, if you have lots of jQuery being used then make sure you wrap it in $(document).ready which will make sure that the DOM is fully loaded before the Javascript is executed
A further point is that HTML/CSS is designed for progressive loading, and if you do it properly then you can get a nice progressive loading of content for your users. I personally wouldn't want my users getting a white screen for a few seconds until everything was loaded. Move your Javascript to the end of the page so that it doesn't block loading and get content onto the screen as quickly as possible.

smooth transition between pages when redirecting with jquery

I am trying to get a smooth transition when I redirect users. First by fading out the page then redirecting and and fadeIn.
Here is my redirect
if ( data.redirect != undefined )
{
$("#toppanel").slideUp(1000);
$("#content").fadeOut(2000, function() {
window.location = data.redirect;
});
My next page has a javascript in the header like this:
jQuery(function ($) {
$("div.container_16").first().hide();
$(".grid_16").first().hide();
$("div.container_16").first().fadeIn(2000);
$(".grid_16").first().slideDown(4000);
This almost work except for a few milli sec where the second page loads then turns blank and fades in. How do I fix this? Do I need to change the css or html?
A simple fix to this would be:
CSS
body{
display:none;
}
JS
jQuery(function ($) {
$('body').show();
$("div.container_16").first().hide();
$(".grid_16").first().hide();
$("div.container_16").first().fadeIn(2000);
$(".grid_16").first().slideDown(4000);
}
You should know that 1 second is a lot of time for a web user. And basically taking 6s extra to just move to another page could be very costly to your user base. I hope you offer a solution without these kind of effects.
UPDATE
CSS
/*
* overflow => so you don't get a scrollbar
* visiblity => so all content is hidden
* background => so you get a black background
*/
.bodyExtra{
overflow:hidden;
visibility:none;
background:#000;
}
JS
jQuery(function ($) {
$(document).ready(function(){
$("div.container_16").first().hide();
$(".grid_16").first().hide();
$('body').removeClass('bodyExtra');
$("div.container_16").first().fadeIn(2000);
$(".grid_16").first().slideDown(4000);
});
}
The logic behind this is to make your page work as a buffer zone. You then hide the elements you want to fade in, remove the class from body and fade everything in.
UPDATE 2013.09.01
I see this answer is still generating some traffic and I have to admit, since the initial answer in 2011, I have an addition to make
HTML/CSS
<noscript>
<style type="text/css">
.bodyExtra{
overflow:auto !important;
visibility:visibile !important;
}
</style>
</noscript>
This can also be done with a <link rel="stylesheet" type="text/css" href="no-js.css" /> tag.
The idea behind this is to fix the disabled javascript issue described by theazureshadow in the comments.
You're getting what is called a "flash of unstyled content" or FUC. You could wrap your second page in a container and hide that via css (display: none;) and then fade in when it's loaded.
Don't use pure css to hide the content originally. If you do, users with JavaScript turned off will not see your content. Instead, only hide when javascript is available.
.js-enabled div.container_16,
.js-enabled .grid_16 {
display: none;
}
Include this line of javascript at the very top of the body:
$(document.body).addClass('js-enabled');
Then in your animation function, after you've hidden .grid_16, include this line to return things to normal:
$(document.body).removeClass('js-enabled');
If you want, you can be more specific and target the hiding styles to the particular elements you want to hide. But I don't know if that's practical for your case -- too few details.

Categories

Resources