I'm doing a project where I need a scrollable slider with play pause button like www.gap.com. I got this below code from W3C but not sure why multiple images are not showing fully. If I change the width value in CSS code, only first image portion scrolls but it totally ignores the 2nd image. Please anyone help me.
var speed=20 // speed of scroller
var step=3 // smoothness of movement
var StartActionText= "Scroll" // Text for start link
var StopActionText = "Pause" // Text for stop link
var x, scroll, divW, sText=""
function onclickIE(idAttr,handler,call){
if ((document.all)&&(document.getElementById)){idAttr[handler]="Javascript:"+call}
}
function addLink(id,call,txt){
var e=document.createElement('a')
e.setAttribute('href',call)
var linktext=document.createTextNode(txt)
e.appendChild(linktext)
document.getElementById(id).appendChild(e)
}
function getElementStyle() {
var elem = document.getElementById('scroller');
if (elem.currentStyle) {
return elem.currentStyle.overflow;
} else if (window.getComputedStyle) {
var compStyle = window.getComputedStyle(elem, '');
return compStyle.getPropertyValue("overflow");
}
return "";
}
function addControls(){
// test for CSS support first
// test for the overlow property value set in style element or external file
if (getElementStyle()=="hidden") {
var f=document.createElement('div');
f.setAttribute('id','controls');
document.getElementById('scroller').parentNode.appendChild(f);
addLink('controls','Javascript:clickAction(0)',StopActionText);
onclickIE(document.getElementById('controls').childNodes[0],"href",'clickAction(0)');
document.getElementById('controls').style.display='block';
}
}
function stopScroller(){clearTimeout(scroll)}
function setAction(callvalue,txt){
var c=document.getElementById('controls')
c.childNodes[0].setAttribute('href','Javascript:clickAction('+callvalue+')')
onclickIE(document.getElementById('controls').childNodes[0],"href",'clickAction('+callvalue+')')
c.childNodes[0].firstChild.nodeValue=txt
}
function clickAction(no){
switch(no) {
case 0:
stopScroller();
setAction(1,StartActionText);
break;
case 1:
startScroller();
setAction(0,StopActionText);
}
}
function startScroller(){
document.getElementById('tag').style.whiteSpace='nowrap'
var p=document.createElement('p')
p.id='testP'
p.style.fontSize='25%' //fix for mozilla. multiply by 4 before using
x-=step
if (document.getElementById('tag').className) p.className=document.getElementById('tag').className
p.appendChild(document.createTextNode(sText))
document.body.appendChild(p)
pw=p.offsetWidth
document.body.removeChild(p)
if (x<(pw*4)*-1){x=divW}
document.getElementById('tag').style.left=x+'px'
scroll=setTimeout('startScroller()',speed)
}
function initScroller(){
if (document.getElementById && document.createElement && document.body.appendChild) {
addControls();
divW=document.getElementById('scroller').offsetWidth;
x=divW;
document.getElementById('tag').style.position='relative';
document.getElementById('tag').style.left=divW+'px';
var ss=document.getElementById('tag').childNodes;
for (i=0;i<ss.length;i++) {sText+=ss[i].nodeValue+" "};
scroll=setTimeout('startScroller()',speed);
}
}
function addLoadEvent(func) {
if (!document.getElementById | !document.getElementsByTagName) return
var oldonload = window.onload
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
oldonload()
func()
}
}
}
addLoadEvent(initScroller)
body {font:1em verdana,sans-serif; color:#000; margin:0}
/* position:relative and overflow:hidden are required */
#scroller { position:relative; overflow:hidden; width:30em; border:1px solid #008080; }
/* add formatting for the scrolling text */
#tag { margin:2px 0; }
/* #testP must also contain all text-sizing properties of #tag */
#testP { visibility:hidden; position:absolute; white-space:nowrap; }
/* used as a page top marker and to limit width */
#top { width:350px; margin:auto; }
<div id="scroller">
<div id="tag">
<img src="https://picsum.photos/1500/600/?image=1"/>
<img src="https://picsum.photos/1500/600/?image=2"/>
</div>
</div>
I got a bit lost in the given JS, and wonder if it is necessary for this relatively simple task.
Here is a method using HTML and CSS for the continuous scrolling and with JS just for the pause/play part.
Because you want continuous scrolling with no gap or jump when the sequence of images goes back to the beginning you need two copies. The tag element is scrolled to the left by half of its width which means that the set of images overwrite themselves so giving a smooth effect.
The JS for the button uses the running value and toggles that.
.playpause {
top: 10%;
left: 10%;
position: absolute;
z-index: 1;
}
#scroller {
width: min(30em, 100vw);
height: min(20em, 100vh);
display: inline-block;
white-space: nowrap;
overflow: hidden;
position: relative;
}
#scroller #tag {
height: 100%;
animation: scroll 10s linear infinite;
animation-fill-mode: forwards;
display: inline-block;
font-size: 0;
}
#keyframes scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
img {
height: 100%;
width: auto;
display: inline-block;
}
<div id="scroller">
<button class="playpause" onclick="document.querySelector('#tag').style.animationPlayState = (document.querySelector('#tag').style.animationPlayState != 'paused') ? 'paused' : 'running';">PLAY/PAUSE</button>
<div id="tag">
<img src="https://picsum.photos/1500/600/?image=1" />
<img src="https://picsum.photos/1500/600/?image=2" />
<!-- second copy of all the imaages -->
<img src="https://picsum.photos/1500/600/?image=1" />
<img src="https://picsum.photos/1500/600/?image=2" />
</div>
</div>
Observation: the site linked to in the question (gap) has a slightly unpleasant 'jump' half way through the images so I think they must be using a different method to achieve continuous scrolling.
Related
I'm pretty new to Javascript, and I have a webpage I'm trying to make that uses the same HTML file, and just cross-fades content instead of redirecting to new pages. I'm using event listeners to know when the current page has faded out and that triggers the new page to come in. Why is it that in the example below, the new pages don't fade in slowly (they just appear suddenly, ignoring the transition property)? Why is the content no longer responding to the CSS transition?
Edit: I'll try to clarify what I'm asking here. I'm aware that the display feature cannot be transitioned, and that's actually why I'm using the event listener at all. I'm trying to make it so that when the content of one page fades out, the next one fades in in the same place, which I believe cannot be achieved with visibility.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<!-- CSS -->
<style>
/* navagation bar style */
#navbar {
overflow: hidden;
background-color: #000000;
text-align: center;
width: 100%;
height: 36px;
}
#navbar a {
display: inline-block;
color: #ffffff;
padding: 10px 15px 10px 15px;
font-size: 16px;
}
/* content style*/
.page {
padding: 50px;
text-align: center;
transition: opacity 1s;
}
</style>
<!-- Javascript -->
<script>
window.onload = function() {setup()};
function setup() {
var page1link = document.getElementById("page1link");
var page1 = document.getElementById("page1");
page1.style["opacity"] = "1";
var page2link = document.getElementById("page2link");
var page2 = document.getElementById("page2");
page2.style["opacity"] = "0";
page2.style["display"] = "none";
var page3link = document.getElementById("page3link");
var page3 = document.getElementById("page3");
page3.style["opacity"] = "0";
page3.style["display"] = "none";
page1link.onclick = function() {fade(page1, page2, page3)};
page2link.onclick = function() {fade(page2, page1, page3)};
page3link.onclick = function() {fade(page3, page1, page2)};
}
function fade(page_1, page_2, page_3) {
let on_page;
if (page_2.style["opacity"] != "0") {
on_page = page_2
} else if (page_3.style["opacity"] != "0") {
on_page = page_3
} if (on_page != undefined) {
on_page.addEventListener('transitionend', fadePageIn)
on_page.style["opacity"] = "0";
function fadePageIn() {
on_page.style["display"] = "none";
page_1.style["display"] = "";
page_1.style["opacity"] = "1";
on_page.removeEventListener('transitionend', fadePageIn);
}
}
}
</script>
<title>Example</title>
</head>
<body>
<div id="navbar">
<a id="page1link" href="javascript:void(0)">Page 1</a>
<a id="page2link" href="javascript:void(0)">Page 2</a>
<a id="page3link" href="javascript:void(0)">Page 3</a>
</div>
<div class="page" id="page1">
page 1 content here
</div>
<div class="page" id="page2">
page 2 content here
</div>
<div class="page" id="page3">
page 3 content here
</div>
</body>
</html>
You can't animate the display property. So when you set opacity and display at the same time, the opacity will transition but the display value changes immediately.
As an alternative, the visibility property can be animated. Interpolation between its values happens at the halfway point, so if you want to make it work with transition that might complicate things. But I've had success in the past using CSS animations to change opacity and visibility at the same time. Using animations like this:
#keyframes becomeVisible {
0% { visibility: visible; }
100% { visibility: visible; }
}
#keyframes becomeHidden {
0% { visibility: visible; }
100% { visibility: visible; }
100% { visibility: hidden; }
}
#keyframes fadein {
0% { opacity: 0; }
100% { opacity: 1; }
}
#keyframes fadeout {
0% { opacity: 1; }
100% { opacity: 0; }
}
This is an interesting question.
Basically, as Mark points out, you can't animate it because setting the display property isn't possible to animate, thus nullifying your other transitions.
Therefore, as long as you can update the transition-able properties later in the event loop, then it will work as intended. A very easy way to do this is to use a setTimeout with 0 time.
function transitionEndCallback() {
oldPage.style["display"] = "none";
newPage.style["display"] = "block";
// add the setTimeout somewhere in this function
setTimeout(() => {
page_1.style["opacity"] = "1";
}, 0)
on_page.removeEventListener('transitionend', transitionEndCallback);
}
This way, it gets called in a different event loop callback, but still updates almost immediately afterwards. There maybe a better callback function or feature because it's a bit hacky, but I can verify it works.
I am running into some issues trying to get a css transition/sequence to start once I scroll over its parent div. I have done this with javascript functions, just never a css transition.
Right now my image will not even show, let alone start at the sequence.If I comment out the javascript the sequence plays as normal.
How can I get the css transitions to start when I get into the parent div section1?
I put a jsfiddle of this in the comments as it is easier to see this.
/*$("#think").hide();
//init scrolling event heandler
$(document).scroll(function() {
var docScroll = $(document).scrollTop(),
boxCntOfset = $("#section1").offset().top - 100;
//when rich top of boxex than fire
if (docScroll >= boxCntOfset) {
$("#think").fadeIn(200)
} else {
$("#think").fadeOut(200)
}
})*/
//Scroll for think images
/*$("#think").hide();
$(function() {
var oTop = $('##section1').offset().top - window.innerHeight;
$(window).scroll(function() {
var pTop = $('body').scrollTop();
console.log(pTop + ' - ' + oTop);
if (pTop > oTop) {
$("#think").fadeIn(200)
}
});
});*/
#red {
width: 100%;
height: 700px;
background:red;
}
#section1 {
width: 100%;
height: 600px;
background:blue;
}
#think {
/*opacity: 0;*/
margin-left: 200px;
width: auto;
height: 500px;
-webkit-animation-name: think;
animation-name: think;
-webkit-animation-duration: 8s;
animation-duration: 8s;
-webkit-animation-direction: normal;
animation-direction: normal;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
/*min-height: 500px; min-width: 500px;*/
background-repeat: no-repeat;
/*background-size: 100% auto;*/
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="red"></div>
<div class="section" id="section1">
<div id="section1-right-container">
<div id="think"></div>
</div>
</div>
You can use the jquery.visible plugin to easily determine if the element is in the viewport: http://www.jsdelivr.com/projects/jquery.visible
Next you will need to determine if is visible in a window scroll function and add a css class that holds the animations:
$(window).on("scroll", function(){
// Determine if the element is in the viewport
if($('.body-main').visible(true)) {
$('.body-main').addClass("think");
}
});
JSfiddle Example: https://jsfiddle.net/Zachary1748/tqh00p9n/
You can use IntersectionObserver object like I show below so when your div element comes into view you can add a class or remove a class:
const observer = new IntersectionObserver(elements => {
elements.forEach( entry => {
// if the element is present
if(entry.isIntersecting){
removableClass.classList.add('class-to-add');
return;
}
// if not then remove the class
removableClass.classList.remove('class-to-remove');
})
})
And to observe the division:
observer.observe(document.querySelector('.any-class'));
And remember that if you want to do some transition then always put the class which shows the content and then remove it afterwards because even if the JS fails to load your content it would still be displayed.
I know it is a repeat question, but I am trying to get my navigation bar to change styling using JavaScript/jQuery/CSS by making jQuery add and remove classes depending on the position of the scrollbar, yet with no prevail. I am a huge noob with jQuery. Could someone tell me if these is something wrong with my code. I have searched for hours and I can't find and error. Here is a working example: http://codepen.io/anon/pen/QbWOJv
And here is my code:
// on scroll,
$(window).on('scroll',function(){
// we round here to reduce a little workload
stop = Math.round($(window).scrollTop());
if (stop > 50) {
$('.nav').addClass('passed-main');
} else {
$('.nav').removeClass('passed-main');
}
.nav
{
background-color: #000000;
opacity: 0.3;
width: 100%;
height: 40px;
position: fixed;
top: 0;
z-index: 2000;
transition: all 0.3s;
}
.nav.past-main
{
background-color: #ffffff;
opacity: 1;
}
<div class="nav">
</div>
Perhaps the example is something that you want to achieve, and when you try it with your code above, it's not working.
Here's the problem with your code in the snippet:
You forgot to close the function
// on scroll,
$(window).on('scroll',function(){
// we round here to reduce a little workload
stop = Math.round($(window).scrollTop());
if (stop > 50) {
$('.nav').addClass('passed-main');
} else {
$('.nav').removeClass('passed-main');
}
}); // You forgot to close the function here
You add/remove class passed-main while in your CSS you're using class selector .nav.past-main
Your window doesn't have any scrollbar, so you need to add this to the CSS to test if it works
body {
height: 1500px;
}
You forgot to include the jQuery in the Snippet.
Here's the working updated snippet
// on scroll,
$(window).on('scroll', function () {
// we round here to reduce a little workload
stop = Math.round($(window).scrollTop());
if (stop > 50) {
$('.nav').addClass('past-main');
} else {
$('.nav').removeClass('past-main');
}
});
.nav {
background-color: #000000;
opacity: 0.3;
width: 100%;
height: 40px;
position: fixed;
top: 0;
z-index: 2000;
transition: all 0.3s;
}
.nav.past-main {
background-color: #ffffff;
opacity: 1;
}
body {
height: 1500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div class="nav"></div>
This is the code I'm currently working with. It works to my purposes of layering the two images. What I am trying to do is have the layer0 opacity lower to 0 as the layer1 opacity increases to 100 over a few seconds. {and then on to layer1 with layer2 and so on eventually looping back to layer0}
Any help would be appreciated.
<head>
<style>
div.layer0
{
width: 371px;
height: 345px;
background:url(image2.jpg);
opacity:1;
filter:alpha(opacity=100); /* For IE8 and earlier */
}
div.layer1
{
width: 371px;
height: 345px;
background:url(image3.jpg);
opacity:0;
filter:alpha(opacity=0); /* For IE8 and earlier */
}
</style>
</head>
<body>
<div class="layer0">
<div class="layer1">
</div>
</div>
</body>
To continually do this in a loop, you'll need some javascript to add an appropriate active class to the image you want displayed. Then using CSS transitions you can achieve the fading between images that you require.
I created a jsfiddle to give you an example of this working: http://jsfiddle.net/pacso/H6dqq/
The basics are as follows.
Some simple HTML divs which you'll be fading:
<div class='red square active'></div>
<div class='yellow square'></div>
<div class='green square'></div>
<div class='blue square'></div>
These are just going to be coloured squares, but yours could contain images.
Next, some CSS markup:
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.green {
background-color: green;
}
.yellow {
background-color: yellow;
}
.square {
width: 200px;
height: 200px;
position: absolute;
top: 20px;
left: 20px;
opacity: 0;
transition: opacity 2s;
-webkit-transition: opacity 2s; /* Safari */
}
.active {
opacity: 1;
}
Note that my transition will alter the opacity of the div itself. You may need to change this as needed.
Now the javascript to make it work on an endless loop:
jQuery(function() {
window.setInterval(function () {
activeSquare = $('.active');
nextSquare = activeSquare.next()
if (nextSquare.length == 0) {
nextSquare = activeSquare.siblings().first();
}
nextSquare.addClass('active');
activeSquare.removeClass('active');
}, 3000);
});
Fairly straightforward. Click the link to my fiddle and hit the run button if you want to see a working demo.
Short answer: not easily.
You're probably better off with javascript for the looping. You could make a delayed keyframe animation, but that won't allow you to loop from the start again: jsfiddle.net/G4PTM (firefox/ie10) -- You could make a lot of keyframes with different timings and you can make it work, but it would require quite a bit of code and not scale well (say you wanted to add another layer/image the code would quickly become unmanagable)
With some javascript, you can just loop through the divs and add and remove a classname to trigger the transitions, like Jon mentioned. Here is a working demo (using jQuery for simplicity, let me know if you need vanilla js)
html
<div class="layer0">
</div>
<div class="layer1">
</div>
<div class="layer2">
</div>
css
div {
width: 371px;
height: 345px;
opacity: 0;
position: absolute;
transition: opacity 2s;
}
div.active {
opacity: 1;
}
div.layer0 {
background:url(http://lorempixel.com/373/345);
}
div.layer1 {
background:url(http://lorempixel.com/372/345);
}
div.layer2 {
background:url(http://lorempixel.com/374/345);
}
js+jquery
var firstDiv = $(".layer0");
var current;
function loopsie() {
// if first iteration or reached end, use first div
if (!current || !current.length) current = firstDiv;
current.addClass("active");
setTimeout(function() {
current.removeClass("active");
setTimeout(function() {
current = current.next();
loopsie(); // recurse
}, 2000);
}, 2000);
}
//initialize
loopsie();
Working demo at http://jsfiddle.net/G4PTM/2/
Plain JavaScript (Without jQuery):
var firstDiv = document.querySelector(".layer0"); // IE 8+
var current;
function loopsie() {
// if first iteration, use first div
if (!current) current = firstDiv;
current.classList.add("active"); // IE 10+, shim at https://developer.mozilla.org/en-US/docs/Web/API/Element.classList
setTimeout(function() {
current.classList.remove("active");
// account for text node (if there is whitespace in html)
if (current.nextSibling && current.nextSibling.nodeName == "DIV") {
current = current.nextSibling;
} else if (current.nextSibling && current.nextSibling.nextSibling && current.nextSibling.nextSibling.nodeName == "DIV") {
current = current.nextSibling.nextSibling;
} else {
// reached end
current = firstDiv;
}
loopsie(); // recurse
}, 2000);
}
//initialize
loopsie();
http://jsfiddle.net/G4PTM/6/
You can use CSS transitions. The example below fades .layer0 in and out in a timespan of 500 ms:
div.layer0 {
opacity: 1;
-webkit-transition:opacity 500ms ease-out;
-moz-transition:opacity 500ms ease-out;
-o-transition:opacity 500ms ease-out;
transition:opacity 500ms ease-out;
}
div.layer0:hover {
opacity: 0;
}
I want to divide my website into two parts: a header containing a large image, and a main part, containing other images, text, etc.
When I scroll the page, the large image on the header should scroll together with the main part. In a certain point, the image should become fixed, and the main part scroll behind it.
I have tried some different approaches, but I can't get the right combination of position, display, top, etc to work.
That's the closest I've got so far: https://jsfiddle.net/aor0abhf/
HTML
<body onscroll='scroll(event)'>
<div class='top' id='top'><img src='http://www.vejanomapa.net.br/wp-content/uploads/2013/03/Maria-Fuma%C3%A7a-em-Tiradentes-MG.jpg'></div>
<div class='bottom'>
<div class='menu'>Menu</div>
<div class='main'><img src='http://tvulavras.com.br/wp-content/uploads/2017/04/maria-fuma%C3%A7a.jpg'></div>
</div>
</body>
Javascript
function scroll(e) {
var T = document.getElementById('top');
var imgH = T.clientHeight; // header image height
var hH = 200; // fixed header height
if (imgH-e.pageY > hH) { // image is scrolling
T.style.top = '-'+e.pageY+'px';
T.style.position = 'sticky';
} else { // image should remain fixed
T.style.top = '-'+(imgH-hH)+'px';
T.style.position = 'fixed';
}
}
CSS
html, body {
margin:0;
}
body {
overflow-y:scroll;
overflow-x:hidden;
}
img {
display:block;
}
.top {
background:#FCC;
display:block;
top:0;
}
.bottom {
display:flex;
min-height:1500px;
background:#CFC;
}
.menu {
width:100px;
background:#CCF;
}
But still there's a glitch in the transition between scroll/fixed positions. And if the left menu (in light blue) could stick together, that would be great! (Maybe subject to another question?)
I updated your fiddle to the following:
No Change to HTML
<body onscroll='scroll(event)'>
<div class='top' id='top'><img src='http://www.vejanomapa.net.br/wp-content/uploads/2013/03/Maria-Fuma%C3%A7a-em-Tiradentes-MG.jpg'></div>
<div class='bottom' id='bottom'>
<div class='menu'>Menu</div>
<div class='main'><img src='http://tvulavras.com.br/wp-content/uploads/2017/04/maria-fuma%C3%A7a.jpg'></div>
</div>
</body>
CSS
html, body {
margin:0;
}
body {
overflow-y:scroll;
overflow-x:hidden;
}
img {
display:block;
}
.top {
background:#FCC;
display:block;
top:0;
}
/* start new rules */
.active{
position: fixed;
}
.active ~ .bottom {
margin-top: 386px;
padding-left: 100px;
}
.active ~ .bottom .menu {
position: fixed;
top: 200px;
bottom: 0;
left: 0;
}
/* end new rules */
.bottom {
display:flex;
min-height:1500px;
background:#CFC;
}
.menu {
min-width:100px;
background:#CCF;
}
Javascript
function scroll(e) {
var T = document.getElementById('top');
var B = document.getElementById('bottom');
var imgH = T.clientHeight; // header image height
var hH = 200; // fixed header height
if (imgH-e.pageY > hH) { // image is scrolling
T.classList.remove('active') // remove class active as applicable
T.style.top = '-'+e.pageY+'px';
T.style.position = 'sticky';
B.style['margin-top'] = '0';
} else { // image should remain fixed
T.classList.add('active') // add class active as applicable
T.style.top = '-'+(imgH-hH)+'px';
}
}
Remove
min-height:1500px;
The div height will stay 1500px;
Try this one
.bottom {
display:flex;
background:#CFC;
}
This should work.
Add margin-top:200px; on <div class='bottom'> part on scroll.