I am very new to JS/JQuery/JQueryUI but have made a few things work on a new site I'm working on.
I've set up a basic navbar where a .click makes different divs slide into view with .show while the other three pop out of existence with .hide. I was extremely proud of myself even though this is super basic.
My issue is that one of these divs contains a YouTube iframe. To get it to stop playing when another div is shown, I just remove the src with .attr (clunky, I know). This means that since the source is re-appended to the iframe each time, going back to that div is slower than I want it to be, and jQuery stutters.
I've put a stripped down version into a JSFiddle. Any suggestions on improving the performance would be greatly appreciated!
PS: The video I have as a placeholder is hilarious and you should enjoy it! :)
HTML:
<div class="button" id="home">1</div>
<div class="button" id="about">2</div>
<div class="button" id="latest">3</div>
<div class="button" id="contact">4</div>
<div class="home"><iframe class="video" id="homeVid"
src="https://www.youtube.com/embed/gspaoaecNAg?controls=0?showinfo=0?rel=0?enablejsapi=1"
frameborder="0" allowfullscreen></iframe></div>
<div class="content about"></div>
<div class="content latest"></div>
<div class="content contact"></div>
CSS
.content {
width: 600px;
height: 480px;
display: none;
clear:both
}
.home, .video {
width: 600px;
height: 480px;
display: flex;
clear:both;
background-color: #CCC
}
.about {background-color: #F00}
.latest {background-color: #0F0}
.contact {background-color: #00F}
.button {
width: 25px;
height: 25px;
border: 1px solid black
}
JavaScript
$(document).ready(function() {
var urlhome = $('#homeVid').attr('src');
$('#home').click(function() {
$('.home').show('slide', {direction: 'right', easing: 'swing'}, 400);
$('.about, .contact, .latest').hide(0);
$('#homeVid').attr('src', urlhome);
});
$('#about').click(function() {
$('.about').show('slide', {direction: 'right', easing: 'swing'}, 400);
$('.home, .contact, .latest').hide(0);
$('#homeVid').attr('src', ' ');
});
$('#latest').click(function() {
$('.latest').show('slide', {direction: 'right', easing: 'swing'}, 400);
$('.home, .contact, .about').hide(0);
$('#homeVid').attr('src', ' ');
});
$('#contact').click(function() {
$('.contact').show('slide', {direction: 'right', easing: 'swing'}, 400);
$('.home, .about, .latest').hide(0);
$('#homeVid').attr('src', ' ');
});
});
Indeed, adding and removing the iframe is costly in terms of performance. Instead we must stop the playback and hide it.
This necessitates to insert it differently into the document, using the YouTube Player API Reference for iframe Embeds. Then we do this:
HTML
<div class="content home">
<div id="player"></div>
</div>
var player;
JavaScript
$(window).load(function(){
player = new YT.Player('player', {
height: '480',
width: '600',
videoId: 'gspaoaecNAg',
});
});
We can simply use player.stopVideo(); whenever we hide the home element. But if only it was so simple.
Using jQuery's hide() has side effects, because the way it hides elements is by setting their CSS to display:none which effectively removes them from the document. This destroys the iframe and recreates it on show(), which presents the same performance issue as before.
We need something more subtle, hiding the elements by putting them aside. For this we use positionning:
.hidden {
position:fixed;
left:200%;
}
This puts them further on the right of the document, outside the viewport and since the units are relative, it can never be vsible no matter how much we stretch the window. This necessitates a few changes in HTML, plus some others for an optimization I will detail further below.
HTML:
<div class="button" id="home">1</div>
<div class="button" id="about">2</div>
<div class="button" id="latest">3</div>
<div class="button" id="contact">4</div>
<div class="content home">
<div id="player"></div>
</div>
<div class="content about hidden"></div>
<div class="content latest hidden"></div>
<div class="content contact hidden"></div>
We have added the class hidden to all elements not visible at the start. We also added a class describing the elements themselves and set to the id of their corresponding button. And we have the content class in each element.
JavaScript:
var player;
$(window).load(function(){
player = new YT.Player('player', {
height: '480',
width: '600',
videoId: 'gspaoaecNAg',
});
});
$(document).ready(function() {
var all = $('.content');
$('.button').click(function() {
all.addClass('hidden');
player.stopVideo();
$('.'+this.id).animate({
'left': '0px',
easing: 'swing'
}, 400, function(){
$(this).removeClass('hidden')
.removeAttr('style');
});
});
});
This has been optimized to avoid checking each element individually. The first part has been explained before, here is how the rest goes:
var all = $('.content');
This selects all the .content elements and keeps them referenced outside the callback in the variable all, so we only have to do this once when the document loads.
We create the callback on all button elements. The next step assumes a click event has been received.
We set all .content elements to hidden. Effectively this should only affect the one currently not hidden.
We stop the video. This will only affect the embedded iframe and we don't bother checking which .content element is active because stopping an already stopped video does nothing special.
Using the id of the button that triggered the click event, we select the corresponding .content element.
We replace show() with animate() and use it to modify the CSS property that is used in the class hidden. This will slide the element from its hidden position to it's normal position.
The animation has a callback executed when it's done. We use it to first remove the hidden class from our now visible element, then to remove the style attribute in which our animation has set left:0px;, as leaving this there would interfere later.
And we're done. This should now be smooth. A demo is available on this JSFiddle.
Related
I'm facing an issue with sliding an image from left to right.
What do I want: Image should slide from the left side of the screen to the right side.
My code is:
$('image').show("slide", { direction: "right" }, 1200);
But this solution is not working a per the expectations. Image slides from left to right, but not the whole image is loaded and the full image is visible only at the end of the animation.
here you can check:
$('#hello').show('slide', {direction: 'right'}, 1000);
you can also use: toggle
$(".slide-toggle").click(function(){
$(".box").animate({
width: "toggle"
});
or:
$(".slidingDiv").toggle("slide");
you can use animate instead of show as using show will show complete image after the animation
$('#image').animate({right:'0px'},1200)
img{
width: 100px;
height: 100px;
position: absolute
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="image" src="https://yt3.ggpht.com/-v0soe-ievYE/AAAAAAAAAAI/AAAAAAAAAAA/OixOH_h84Po/s900-c-k-no-mo-rj-c0xffffff/photo.jpg"/>
I think your problem is that the animation started before image object was loaded completely to the browser.
You should check out jquery load event: https://api.jquery.com/load-event/
And search for answers for question "jquery image load callback",
e.g.: jQuery or Javascript check if image loaded
In my opinion the best way is create image object with JS, push it to DOM element and start animation, when image will be loaded completely.
In short:
$("<img/>")
.attr("src", "/images/your-image.jpg")
.on('load', function() { startAnimation() })
.appendTo($('#imageContainer'));
var startAnimation = function(){
$('#hello').show('slide', {direction: 'right'}, 1000);
}
$(document).ready(function() {
$("img").animate({
marginLeft: "0px"
}, 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<div class="frame" style="width: 300px; height:300px; overflow:hidden;">
<img id="image" src="https://www.filterforge.com/more/help/images/size400.jpg" style='margin-left:-300px; height: 100%; width: 100%; '>
</img>
</div>
I'm trying to slide a div from the left to the right side when the 'submit' button is clicked. After a little pause, the div would automatically slides back to it's original position. Currently it goes to the right side but it isn't coming back to the left corner.
CSS
#mainform{
position: absolute;
display: block;
padding-top:20px;
font-family: 'Fauna One', serif;
}
HTML
<div id="mainform">
<!-- Required div starts here -->
<form id="form">
<h3>Contact Form</h3>
<div class="hello"></div>
<input type="button" id="submit" value="Send Message"/>
</form>
</div>
JS
$(document).ready(function() {
$('#submit').click(function(e) {
reslide();
function reslide() {
$('#mainform').delay().animate({width: '510px', left: '1050'}, 600).delay(5000).animate({width: '510px', right: '1000px'}, 200, function() {
setTimeout(reslide, 3000);
});
}
$('.hello').fadeIn(1500);
$("<b>Successfully send</b>").appendTo(".hello");
$('.hello').fadeOut(2500);
});
});
When you give feedback to the user after/before submiting, try to use CSS3 Transform instead of actually moving/resizing the object.
function slide($obj) { // jQuery object of element
$obj.css("transform", "translateX (50px)");
setTimeout(function(){
$obj.css("transform", "none");
}, 5000);
}
To make it smooth (real animation) apply CSS3 Transition property.
<style>
.object {
transition: transform 0.6s;
}
</style>
Or you can shorten, if you're sure everything'd go smoothly.
function slide($obj) { // jQuery object of element
$obj.animate("transform", "translateX (50px)")
.delay(600).
.animate("transform", "translateX (0px)");
}
PS; in my expirience jQuery.delay(); wasn't always working with queueing animations, i'm not entirely sure why. As a matter of fact, this happened only sometimes. Sometimes tought it wasn't working
// not working
$("smth").animate({"rule":"val"}).delay(500).animate("rule":"val");
// working
$("smth").animate({"rule":"val"})
setTimeout(function(){
$("smth").animate({"rule":"val"})
}, 1000);
The reason it's not working is that, while you add right to the element, you also keep left with its original value, thus the element will not "come back". Add left: '', to the 2nd animate function and you should be good to go:
function reslide() {
$('#mainform').delay().animate({
width: '510px',
left: '1050'
}, 600).delay(5000).animate({
width: '510px',
left: '',
right: '1000px'
}, 200, function () {
setTimeout(reslide, 3000);
});
}
Here is a fiddle you can play with: http://jsfiddle.net/bv8dwaq2/
I am trying to use jQuery slide-in animation and it seems to work fine, but my the second animation doesn't work. Can anyone tell me what I am doing wrong?
This line:
$('#headline1Txt').animate({'marginLeft': "100px"}, 1000);
is working fine, but this one:
$("#headline1Txt").animate({left: "+=30"}, 500);
is not working.
My Code
HTML
<div id="mainContainer">
<div id="headlineText">
<p id="headline1Txt" >Striped Bag</p>
</div>
</div>
JS
$(document).ready(function () {
$('#headline1Txt').animate({'marginLeft': "100px"}, 1000);
$("#headline1Txt").animate({left: "+=30"}, 500);
});
CSS
#headlineText {
margin: 60px 80px;
}
The left CSS property specifies part of the position of positioned
elements.
For absolutely positioned elements (those with position: absolute or position: fixed), it specifies the distance between the left margin
edge of the element and the left edge of its containing block.
https://developer.mozilla.org/en-US/docs/Web/CSS/left
As the CSS left property is part of position:*; property's, fix by adding the position property, so jquery knows what to increment a left value to.
In Action
CSS
#headlineText
{
margin:60px 80px;
}
#headline1Txt
{
position:relative;
}
HTML
<div id="mainContainer">
<div id="headlineText">
<p id="headline1Txt">Striped Bag</p>
</div>
</div>
jQuery
$(document).ready(function () {
$('#headline1Txt').
animate({ 'marginLeft': "100px" }, 1000).
animate({ left:"+=30" }, 5000);
});
What are you trying to achieve?
You want the text to slide in (heading right across the screen) then slide another 30px?
Thats what $("#headline1Txt").animate({ 'marginLeft': "+=30" }, 500); will achieve.
'marginLeft' not just left.
The left property of css only works with elements with absolute position. To make your animation work you have to put you element at absolute position.
So this post might get lengthy but I'm stuck with iScroll. What I'm doing is populating my list with articles and when one gets clicked, I'm sliding in a div over the list to display the article. That part works but what doesn't is when I scroll through the article and get to the end, it keeps scrolling the list with articles. You can have a look here (the site is in russian but click on an article and scroll all the way to the bottom). Here's my entire code:
<head>
<style>
body{
padding: 0;
margin: 0;
border: 0;
}
#header{
position:fixed;
top:0;
left:0;
height:100px;
width: 100%;
background-color: black;
}
header{
position: absolute;
z-index: 2;
top: 0; left: 0;
width: 100%;
height: 50px;
}
#wrapper{
position: absolute;
z-index: 1;
width: 100%;
top: 52px;
left: 0;
overflow: auto;
}
#container{
position:fixed;
top:0;
right:-100%;
width:100%;
height:100%;
z-index: 10;
background-color: red;
overflow: auto;
}
#content{
margin:100px 10px 0px 10px;
}
</style>
</head>
<body>
<header>Main News</header>
<div id="wrapper">
<ul id="daily"></ul>
<ul id="exclusive"></ul>
<ul id="must"></ul>
<ul id="main"></ul>
<ul id="ukr"></ul>
<ul id="nba"></ul>
<ul id="euro"></ul>
</div>
<div id="container">
<div id="wrapper2">
<div id="header">
<button onclick="hide();">Back</button>
</div>
<div id="content"></div>
</div>
</div>
<script src="js/zepto.js"></script>
<script>
//AJAX requests to fill the li's...
function sayhi(url){
$('#container').animate({
right:'0',
}, 200, 'linear');
$.ajax({
url: serviceURL + "getnewstext.php",
data: {link: url},
success: function(content){
$('#content').append(content);
}
});
}
function hide(){
$('#container').animate({
right:'-100%'
}, 200, 'linear');
$('#content').empty();
}
</script>
<script src="js/iscroll-lite.js"></script>
<script>
var myScroll;
function scroll () {
myScroll = new iScroll('wrapper2', {hScroll: false, vScrollbar: false, bounce: false});
myScroll2 = new iScroll('wrapper', {hScroll: false, vScrollbar: false});
}
document.addEventListener('DOMContentLoaded', scroll, false);
</script>
</body>
Is there a way to scroll on the div container, or content, or wrapper2 without scrolling the wrapper div with the list of articles? Maybe I'm not using iScroll correctly? The same problem happens on Android and iPhone.
EDIT 1:
I set the wrapper position to fixed. Now the div container is the only one scrolling but the list of articles isn't scrolling...I added another iScroll to the wrapper but it's not working. Any advice here?
EDIT 2:
So I dropped iScroll all together and trying with CSS instead. To my onclick events I added:
$('body').css('overflow', 'hidden');
And when the close button is clicked I changed the overflow to auto. Now this stops the body from scrolling in a browser but not on mobile!!! How can I make it do the same thing on mobile???
I finally got it to work. What I needed to do is add another div inside the wrapper div. I'll share the code so hopefully it helps someone else Here's what the new code looks like:
<body>
<!--Added scroller div(without iScroll it works also...just make two divs so the body isn't scrolled but the second div is scrolled-->
<div id="wrapper">
<div class="scroller">
<header>Main News</header>
<ul id="daily"></ul>
<ul id="exclusive"></ul>
<ul id="must"></ul>
<ul id="main"></ul>
<ul id="ukr"></ul>
<ul id="nba"></ul>
<ul id="euro"></ul>
</div>
</div>
<div id="container">
<div class="scroller">
<div id="header">
<button onclick="hide();">Back</button>
</div>
<div id="content"></div>
</div>
</div>
<script>
$('body').on('touchmove', function(e){
e.preventDefault();
});
//prevents native scrolling so only iScroll is doing the scrolling
//after the AJAX call to get the content, declare your iScroll variable
var myScroll;
myScroll = new iScroll('wrapper');
setTimeout (function(){
myScroll.refresh();
}, 2000);
//set time out to give the page a little time to load the content and refresh your iScroll variable so it takes in the entire content of the wrapper div
var myScroll1;
myScroll1 = new iScroll('container');
//I defined my second iScroll variable here so I can destroy it in the next part...
//function sayhi(url) stays the same but in success of AJAX looks like this:
success: function(content){
$('#content').append(content);
myScroll1.destroy();
myScroll1 = null;
myScroll1 = new iScroll('container');
setTimeout (function(){
myScroll1.refresh();
}, 2000);
}
//when the div slides on the screen and content gets loaded, destroy your second iScroll
//variable, set it to null and define it all over again so it takes in the entire content
And that's it. Works perfectly now with two divs which need to use iScroll on the same page. Hope the explanation is clear enough and helps someone!!!
How can I fade one image into another with jquery? As far as I can tell you would use fadeOut, change the source with attr() and then fadeIn again. But this doesn't seem to work in order. I don't want to use a plugin because I expect to add quite a few alterations.
Thanks.
In the simplest case, you'll need to use a callback on the call to fadeOut().
Assuming an image tag already on the page:
<img id="image" src="http://sstatic.net/so/img/logo.png" />
You pass a function as the callback argument to fadeOut() that resets the src attribute and then fades back using fadeIn():
$("#image").fadeOut(function() {
$(this).load(function() { $(this).fadeIn(); });
$(this).attr("src", "http://sstatic.net/su/img/logo.png");
});
For animations in jQuery, callbacks are executed after the animation completes. This gives you the ability to chain animations sequentially. Note the call to load(). This makes sure the image is loaded before fading back in (Thanks to Y. Shoham).
Here's a working example
$("#main_image").fadeOut("slow",function(){
$("#main_image").load(function () { //avoiding blinking, wait until loaded
$("#main_image").fadeIn();
});
$("#main_image").attr("src","...");
});
Well, you can place the next image behind the current one, and fadeOut the current one so that it looks like as though it is fading into the next image.
When fading is done, you swap back the images. So roughly:
<style type="text/css">
.swappers{
position:absolute;
width:500px;
height:500px;
}
#currentimg{
z-index:999;
}
</style>
<div>
<img src="" alt="" id="currentimg" class="swappers">
<img src="" alt="" id="nextimg" class="swappers">
</div>
<script type="text/javascript">
function swap(newimg){
$('#nextimg').attr('src',newimg);
$('#currentimg').fadeOut(
'normal',
function(){
$(this).attr('src', $('#nextimg').attr('src')).fadeIn();
}
);
}
</script>
Are you sure you're using the callback you pass into fadeOut to change the source attr and then calling fadeIn? You can't call fadeOut, attr() and fadeIn sequentially. You must wait for fadeOut to complete...
Old question but I thought I'd throw in an answer. I use this for the large header image on a homepage. Works well by manipulating the z-index for the current and next images, shows the next image right under the current one, then fades the current one out.
CSS:
#jumbo-image-wrapper
{
width: 100%;
height: 650px;
position: relative;
}
.jumbo-image
{
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
}
HTML:
<div id="jumbo-image-wrapper">
<div class="jumbo-image" style="background-image: url('img/your-image.jpg');">
</div>
<div class="jumbo-image" style="background-image: url('img/your-image-2'); display: none;">
</div>
</div>
Javascript (jQuery):
function jumboScroll()
{
var num_images = $("#jumbo-image-wrapper").children(".jumbo-image").length;
var next_index = jumbo_index+1;
if (next_index == num_images)
{
next_index = 0;
}
$("#jumbo-image-wrapper").children(".jumbo-image").eq(jumbo_index).css("z-index", "10");
$("#jumbo-image-wrapper").children(".jumbo-image").eq(next_index).css("z-index", "9");
$("#jumbo-image-wrapper").children(".jumbo-image").eq(next_index).show();
$("#jumbo-image-wrapper").children(".jumbo-image").eq(jumbo_index).fadeOut("slow");
jumbo_index = next_index;
setTimeout(function(){
jumboScroll();
}, 7000);
}
It will work no matter how many "slides" with class .jumbo-image are in the #jumbo-image-wrapper div.
For those who want the image to scale according to width percentage (which scale according to your browser width), obviously you don't want to set height and width in PIXEL in CSS.
This is not the best way, but I don't want to use any of the JS plugin.
So what can you do is:
Create one same size transparent PNG and put an ID to it as
second-banner
Name your original image as first-banner
Put both of them under a DIV
Here is the CSS structure for your reference:
.design-banner {
position: relative;
width: 100%;
#first-banner {
position: absolute;
width: 100%;
}
#second-banner {
position: relative;
width: 100%;
}
}
Then, you can safely fade out your original banner without the content which placed after your image moving and blinking up and down