I'm trying to alter the padding of an element based on the scroll position of the page; as the user scrolls further down the page, the padding increases, and as they scroll back up, the padding decreases.
My main problem is that the scrolling isn't very smooth, and occasionally if I scroll to the top of the page too fast, the padding of the element is a different size each time. My goal is to set a minimum and maximum amount of padding, so the scrolling is essentially a transition between two sizes. Can anyone see where I'm going wrong?
Here's my jQuery so far:
$(window).scroll(function(){
var h = 45;
if($(window).scrollTop() < 200){
$(".header").css({
'paddingTop': h-$(window).scrollTop() + "px",
'paddingBottom': h-$(window).scrollTop() + "px"
});
}
});
And here's my jsfiddle: http://jsfiddle.net/JDE8h/3/
Here you go:
JavaScript:
var _window = $(window),
header = $('.header'),
max = 50,
padding = parseFloat(header.css('padding-top')),
currentPadding = padding,
scrollPos = _window.scrollTop();
_window.scroll(function() {
if (scrollPos < _window.scrollTop() && currentPadding < max) {
header.css('padding', ++currentPadding + 'px 0');
} else if (scrollPos > _window.scrollTop() && currentPadding > padding) {
header.css('padding', --currentPadding + 'px 0');
}
if (_window.scrollTop() == 0)
header.css('padding', padding + 'px 0');
scrollPos = _window.scrollTop();
});
And add CSS transition property to .header:
.header{
/* other CSS declarations ...*/
-webkit-transition: padding .3s linear;
-moz-transition: padding .3s linear;
-ms-transition: padding .3s linear;
-o-transition: padding .3s linear;
transition: padding .3s linear;
}
JSFiddle Demo.
I believe you've got the numbers backwards. You are getting a negative number from your calculation. For example:
if h = 45, then 45 - $scrollTop (from your active target of 20 to 200) is very quickly a negative number.
Based on the example of the Carrera World website, I think believe is the effect you are looking for?
jQuery
$(window).scroll(function(){
var h = 45;
var $scrollTop = $(window).scrollTop();
if($scrollTop < 200 && $scrollTop > 20){
$(".header").css({
'paddingTop': $scrollTop - h + "px"; // Subtract h from scrollTop
'paddingBottom': $scrollTop - h + "px";
});
}
$('.header').text($scrollTop); // Display scrollTop value in header (for demo).
});
I also gave your header a min-height to keep it from getting too small in edge cases.
CSS
.header {
...
line-height: 40px; /** Keep things centered vertically **/
min-height: 40px;
}
Hopefully this helps you!
JSFiddle example.
You could use a library dedicated to this task, such as skrollr. It may not be worth it though if it's just the padding.
<div data-0="padding:0px;" data-200="padding:500px;"></div>
Will animate the padding from 0 to 500 pixels while scrolling from 0 to 200.
Here's a fiddle http://jsfiddle.net/JDE8h/9/
Related
I have a simple JS script which listens to keyboard input and displays, at a random position, a short animation of every typed letter fading out and getting smaller.
'use strict'
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
document.addEventListener('keydown', function(e) {
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
.anim {
position: fixed;
text-align: center;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0%;
}
#keyframes fade {
0% { opacity: 100%; font-size: 300px;}
100% { opacity: 0%; font-size: 100px;}
}
By animating the font-size property, each letter gets smaller. However, since its "anchor point" is the top of the div, the visible effect is a letter getting smaller and moving slightly upwards. I would like each letter to shrink towards the vertical center of the div instead.
I can calculate the center the div easily and add the proper top coordinate to the #keyframe property, but I don't know how to modify that property in JS, individually for each div. Is this possible at all via CSS? Or should I rewrite the whole thing in pure JS?
You don't need to adjust the div's top value at all. As there is no border or anything else displayed for the DIV tag itself - just the letter within it - you can adjust either the margin, the border and/or the padding to achieve the same effect as increasing the top value for the DIV. As each of these can be handled within the css transition, you could do something like:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
document.addEventListener('keydown', function(e) {
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key;
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
.anim {
display:block;
position: fixed;
text-align: center;
width:200px;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0;
margin:0px;
}
#keyframes fade {
0% { opacity:1; font-size: 300px;}
100% { opacity:0; font-size: 100px; margin-top:100px;}
}
The initial state of the DIV is with margin:0px. Adding a margin-top setting to the keyframes css, increases this from 0 to 100 during the transition. The effect of that is to push the DIV down - and, as noted above, as nothing is being displayed for the DIV itself, the user will not see it move. Note that I have fixed the width of the DIV at 200px so ensure that everything is always centered horizontally - otherwise the DIV width is based on the width of the character, so would change during transition and the character would move to the left as the centre line changes. I've moved some of the code around to make it easier to test - but the only actual change is in the CSS styling. Also note that opacity is a value from 0 to 1, so should not be shown as a percentage.
UPDATE
Have a look at the following snippet. I think that it may be possible to have random font sizes AND random positions using transform rather than animate.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
function zoomOUT(){
let d = document.getElementById("test");
d.classList.remove("zoomIN");
d.classList.add("zoomOUT");
}
function zoomIN(){
let d = document.getElementById("test");
let dletter = document.getElementById("testletter");
let t = getRandomInt(20, 60) * 10;
let l = getRandomInt(20, 100) * 10;
let fs = getRandomInt(10, 20) * 10;
dletter.innerHTML = letters[getRandomInt(0, 61)];
d.style.top = t + "px";
d.style.left = l + "px";
d.style.fontSize = fs + "%";
d.classList.remove("zoomOUT");
d.classList.add("zoomIN");
}
#test {
position:absolute;
padding: 50px;
margin: 0 auto;
text-align:center;
vertical-align:middle;
}
.zoomIN {
opacity:1;
transform: scale(3);
transition: transform 2s;
}
.zoomOUT {
opacity:0.5;
transform: scale(0.1);
transition: transform 3s;
}
<button onclick="zoomOUT();" z-index=1>Play</button><button onclick="zoomIN();" z-index=1>Restart</button>
<div id="test" class="zoomIN" style="top:300px; left:300px;" z-index=0><div id="testletter" style="font-size:600%; width:100%; height:100%">A</div></div>
Transform seems to keep things in the same place, so there is no need to adjust any top/margin/border/padding settings at all. In fact, the only things that change are the font-size (using scale(..)) and opacity. The size of the font is determined by the code. Note that this requires the character to be in a div within a div. This is just a test, but should give you enough to convert things into your code requirements.
I am trying to make a function which starts to increase (and immediately decrease after opacity equal 1) opacity of div on scroll at the specific place of the window, but my function works only with the first div. I mean second and third divs start changing their opacity at the same time with the first div. But they should do it only if they at the viewport. How to make it works with bottom divs properly?
$(document).ready(function() {
$(window).on('scroll', function() {
var first = $('div.first')
var second = $('div.second')
var third = $('div.third')
var op1 = opacityUp(first, 1, window.innerHeight, 1.5)
var op2 = opacityUp(second, 1, 1.5, 1.5)
var op3 = opacityUp(third, 1, 1.5, 1.5)
})
})
function opacityUp(div, opacityLevel, topMargin, opacitySpeed) {
// div position where to start increasing opacity
const elPosition = (window.pageYOffset - window.innerHeight / topMargin);
div.css({
opacity: function() {
let val = 0 + (elPosition/(window.innerHeight*opacitySpeed));
console.log(elPosition)
if (val <= opacityLevel) {
return val
} else {
return opacityLevel
}
}
});
}
div {
height: 100vh;
opacity: 0;
display: flex;
align-items: center;
justify-content: center;
}
.first {
background: red;
}
.second {
background: green;
}
.third {
background: black;
}
<!-- begin snippet: js hide: false console: true babel: false -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<div class="first"></div>
<div class="second"></div>
<div class="third"></div>
I think the problem might have to do with the fact that elPosition doesn't take the actual element's position into account. If you want to define the position where the element begins to reduce opacity as the point at which the div enters the viewport, I'd change it to something like:
const elPosition = (window.pageYOffset - div.offset().top) + window.innerHeight;
Now, elPosition starts negative and reaches 0 when the div enters the viewport at the bottom. Then, elPosition increases by the number of pixels it travels up the screen (as the user scrolls down).
You can set the opacity by doing something like:
let val = (window.innerHeight - elPosition) / window.innerHeight;
if you want the opacity to fade to 0 and finish when the top of the element reaches the top of the viewport. You can add the div's height to the equation to start the opacity fading in the middle or bottom of the div.
I have a custom cursor that I implemented using some JS found on here. It work's perfectly - BUT, I need to turn it off on touch screens, otherwise it just sits on the screen as a big yellow dot.
Unfortunately, apart from the inline styles, I don't understand the JS enough to edit it to achieve this.
This is the JS
$("body").append('<div class="cursor"></div>');
$("html").append('<style>html, body, .msty_notcur {cursor:none !important;}.cursor {z-index:10000000000000; mix-blend-mode: difference; position: fixed;background-color:#FDFF07; width:25px;height:25px;border-radius:100%;transform:translate(-50%,-50%);top:0px;left:0px;pointer-events:none; -webkit-transition: width 200ms, height 300ms; -webkit-transition: height 200ms, width:300ms; } .overlink {width:45px;height:45px; -webkit-transition: width 300ms, height 300ms; -webkit-transition: height 200ms, width:200ms;} .overtext {background-color:rgba(100,100,255,0.25) !important;border: 1px solid rgba(0,0,100,0.25) !important;}</style>');
var scrollY = 0, scrollX = 0;
$(document).mousemove(function(e){
$(".cursor").css("top",e.pageY - scrollY + "px").css("left",e.pageX - scrollX + "px");
});
$(document).scroll(function(e){
scrollY = $(window).scrollTop();
scrollX = $(window).scrollLeft();
});
setInterval(function(){scroll = $(window).scrollTop();}, 1000);
$("*").hover(function(e){
var index = -1;
try {
index = $(this).attr("class").toLowerCase().indexOf("button");
if (index == -1) {
index = $(this).attr("class").toLowerCase().indexOf("link");
}
}
catch(e) {
index = -1;
}
if($(this).css("cursor") == "pointer" || $(this).get(0).tagName == "A" || $(this).get(0).tagName == "BUTTON" || $(this).hasClass("msty_cur") || index > -1) {
$(this).addClass("msty_cur");
$(this).css("cursor","none");
$(".cursor").addClass("overlink");
}
if($(this).css("cursor") != "none") {
$(this).addClass("msty_notcur")
}
}, function(e){
$(this).css("cursor","none");
$(".cursor").removeClass("overlink");
});
And the website where it is implemented is here
The difficulty I have with using just CSS is applying the mix-blend-mode to the cursor
Using JavaScript, your best bet is to try and detect the screen size. It looks like 768 is a safe bet for max-width.
You'd want to add both a conditional statement (for when the page loads) onresize for the document. I'd also wrap the cursor creation into a function for re-usability/convenience.
customCursor = () => {
$("body").append('<div class="cursor"></div>');
$("html").append('<style>.cursor {z-index:10000000000000; mix-blend-mode: difference; position: fixed;background-color:#FDFF07; width:25px;height:25px;border-radius:100%;transform:translate(-50%,-50%);top:0px;left:0px;pointer-events:none; -webkit-transition: width 200ms, height 300ms; -webkit-transition: height 200ms, width:300ms; } .overlink {width:45px;height:45px; -webkit-transition: width 300ms, height 300ms; -webkit-transition: height 200ms, width:200ms;} .overtext {background-color:rgba(100,100,255,0.25) !important;border: 1px solid rgba(0,0,100,0.25) !important;}</style>');
var scrollY = 0,
scrollX = 0;
$(document).mousemove(function(e) {
$(".cursor").css("top", e.pageY - scrollY + "px").css("left", e.pageX - scrollX + "px");
});
$(document).scroll(function(e) {
scrollY = $(window).scrollTop();
scrollX = $(window).scrollLeft();
});
setInterval(function() {
scroll = $(window).scrollTop();
}, 1000);
$("*").hover(function(e) {
var index = -1;
try {
index = $(this).attr("class").toLowerCase().indexOf("button");
if (index == -1) {
index = $(this).attr("class").toLowerCase().indexOf("link");
}
} catch (e) {
index = -1;
}
if ($(this).css("cursor") == "pointer" || $(this).get(0).tagName == "A" || $(this).get(0).tagName == "BUTTON" || $(this).hasClass("msty_cur") || index > -1) {
$(this).addClass("msty_cur");
$(this).css("cursor", "none");
$(".cursor").addClass("overlink");
}
if ($(this).css("cursor") != "none") {
$(this).addClass("msty_notcur")
}
}, function(e) {
$(this).css("cursor", "none");
$(".cursor").removeClass("overlink");
});
}
if (window.innerWidth <= 768) {
document.getElementsByTagName('BODY')[0].style.cursor = 'default'
} else {
customCursor()
}
$(window).resize(function() {
if (window.innerWidth <= 768) {
var cursor = $(".cursor")
cursor.remove()
$('body').css({
cursor: "default",
})
} else {
$('body').css({
cursor: "none",
})
customCursor()
}
});
Honestly, your best bet is to utilize CSS more as opposed to using jQuery to append/manipulate things but that's up to you.
I'm trying to change the opacity of a DIV based on how much is visible (height-wise) in the window. For example if 50% of the DIV is visible in the window then the opacity should be .5
Here is what I've got, I know it's amateur and not optimal code. It's my math that is the problem. When the DIV is roughly 50% on the screen, with my calculations it comes out to around 80%
$(window).scroll(function () {
var block = $('.block')
var blockHeight = block.outerHeight();
var bottom_of_block = block.offset().top + blockHeight;
var blockOpacity = 0;
if (bottom_of_block < ($(window).height() + $(window).scrollTop())) {
// Sets opacity to 1 if div is completely on screen
blockOpacity = 1;
} else {
// This is the math that I cant figure out completely
blockOpacity = ($(window).height() + $(window).scrollTop()) / (bottom_of_block);
}
block.css('opacity', blockOpacity);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="height: 500px"></div>
<div class="block" style="background: blue;height: 220px;width: 220px;"></div>
I think you need to calculate blockOpacity like this:
blockOpacity = ($(window).height() + $(window).scrollTop() - block.offset().top) / blockHeight;
I would like to add left margin and right margin to the body to hide the width change when I hide the vertical scrollbar.
I have this code that finds the width of the vertical scrollbar:
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
It gives the value "17" (in pixels) for IE11, Chrome 45, and Firefox 39 (desktop).
When I hide the vertical scrollbar, all elements, such as images, jump exactly 17 pixels to the right, which I want to hide.
I have tried:
document.body.style.marginRight = scrollbarwidth + "px";
$('body').css('margin-right', scrollbarwidth);
$(body).css("marginRight", scrollbarwidth + "px");
The last one might be faulty in some way, since other parts of the function stops working when it's enabled. The two others don't seem to work either, as I don't see any margin changes.
EDIT 1: For easier understanding of how I am going to use it, I wanted to mention that it's supposed to trigger on a on scroll function, like this:
var check1 = false;
$(document).bind('scroll', function() {
if(check1 === false && $(window).scrollTop() >= $('#divscrolltester').offset().top + $('#divscrolltester').outerHeight() - window.innerHeight) {
check1 = true;
unloadScrollBars();
disableScroll();
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
//document.body.style.paddingRight = scrollbarwidth + "px"; Temporary disabled.
//$('body').css('padding-right', scrollbarwidth); Temporary disabled.
//$(body).css("marginRight", scrollbarwidth + "px"); Temporary disabled.
setTimeout(function() {
enableScroll();
reloadScrollBars();
//document.body.style.paddingLeft = scrollbarwidth + "px"; Temporary disabled.
//$('body').css('padding-left', scrollbarwidth); Temporary disabled.
//$(body).css("marginLeft", scrollbarwidth + "px"); Temporary disabled.
}, 500);
}
});
EDIT 2:
Here is a Fiddle to show most of the js, html and css: https://jsfiddle.net/tfnwj7dj/10/.
I haven't added the change of css through code yet, as I'm still trying to solve the issue. Also, the scrolling and scrollbar are supposed to be re-enabled in a second, but there seems to be an error in there somewhere, sorry.
EDIT 3:
For your information at this moment, these lines work:
document.body.style.paddingLeft = (scrollbarwidth) + "px";
$('body').css('padding-left', scrollbarwidth);
document.body.style.paddingRight = (scrollbarwidth) + "px";
$('body').css('padding-right', scrollbarwidth);
document.body.style.marginLeft = (scrollbarwidth) + "px";
$('body').css('margin-left', scrollbarwidth);
document.body.style.marginRight = (scrollbarwidth) + "px";
$('body').css('margin-right', scrollbarwidth);
Maybe you have enough information to solve it, if you have the same issue, but unfortunately, this wasn't enough for me. It might be important info to know that I have my content centered with a width / max-width of just 500px, and that I don't actually have a body class. Maybe on designs with width="100%", or elements with absolute positioning, the lines might be enough.
Both javascript and jquery solutions are welcomed.
EDIT 4:
I finally solved it for my own circumstances - feel free to read the answer below. It works for preventing elements to jump when hiding the vertical scrollbar, and with some tinkering, it could probably do for a body class, or other situations.
Is your scrollbarwidth integer? Try this
var scrollbarwidth = 100;
$('body').css('margin-right', scrollbarwidth);
Maybe you have wrong value at scrollbarwidth ? In my ff this code works.
I managed to solve it - I'd like to clarify that my css actually don't contain a body class, and that I just centered all elements with a width / max-width of 500px and margin-left/right auto.
For my and other, similar cases, here is the answer:
/* First 5 lines for finding the scrollbar width. */
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
var scrollbarwidthadjustment = scrollbarwidth / 2; /* For centered elements, divide the scrollbar width by 2. */
var element = document.getElementById('element');
element.style.right = (scrollbarwidthadjustment) + "px";
And when you re-enable the vertical scrollbar, simply add:
element.style.right = "0px";
Also, the element must have a css position stated, otherwise it won't trigger. Here is an example of a css style that works:
.examplestyle {
color: white;
position: relative;
margin-left: auto;
margin-right: auto;
max-width: 100%;
display: block;
}
EDIT 1:
To prevent some unsightly css errors on mobile devices, add these lines:
/* ... */
var scrollbarwidthadjustment = scrollbarwidth / 2;
var windowWidth = $(window).width(); /* Get current window width on click/scroll etc. */
var window1 = windowWidth + scrollbarwidth; /* Window width + scrollbar width. */
var element = document.getElementById('element');
if(window1 >= widthofelement) {element.style.right = (scrollbarwidthadjustment) + "px";}
else {}
EDIT 2:
Fix for image resized smaller than its original size:
var offsetwidth = element.offsetWidth;
var widthadjustment = offsetwidth - scrollbarwidth; /* Get full width of image when scrollbar hidden, and then remove the scrollbar width. */
if(window1 < widthofelement && scrollbarwidth > 0) {
element.style.width = widthadjustment + "px";
element.style.right = (scrollbarwidthadjustment) + "px";
}
And then this code when showing the Y-scrollbar again:
if(window1 < widthofelement && scrollbarwidth > 0) {
element.style.width = "OriginalSizepx";
element.style.right = "0px";
}
If you want to use every edit that I have added, here is the full code:
/* First 5 lines for finding the scrollbar width. */
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
var scrollbarwidthadjustment = scrollbarwidth / 2; /* For centered elements, divide the scrollbar width by 2. */
var element = document.getElementById('element'); /* Put element ID into a variable for easier use, and consecutive uses without re-identifying it. */
var window1 = windowWidth + scrollbarwidth; /* Window width + scrollbar width. */
var offsetwidth = element.offsetWidth; /* Get exact element size in current window. Shows shown dimensions when the window is resized. */
var widthadjustment = offsetwidth - scrollbarwidth; /* Get full width of image when scrollbar hidden, and then remove the scrollbar width. */
if(window1 >= widthofelement) {element.style.right = (scrollbarwidthadjustment) + "px";} /* If current window is equal to or greater than element width... */
if(window1 < widthofelement && scrollbarwidth > 0) { /* If current windows is smaller than the element width, and the window has a scrollbar greater than 0 pixels in width. */
element.style.width = widthadjustment + "px";
element.style.right = (scrollbarwidthadjustment) + "px";
}
/* When re-enabling the Y-scrollbar again; */
if(window1 >= widthofelement) {element.style.right = "0px";}
if(window1 < widthofelement && scrollbarwidth > 0) {
element.style.width = "OriginalSizepx";
element.style.right = "0px";
}
For further clarification, this code will prevent elements from jumping to the right when you hide the vertical scrollbar.
padding is your answer, as Shikkediel said. Just change margin to that and it'll work.
The items move because you change the default margin body has, so the whole body moves to the left (in case we are modifying margin-right).
If you remove the scroll bar, the default margin will go right behind it, and then you need to "buffer" the rest, left of the margin, and that's what padding does.
I really enjoy working with the Inspecting tool Chrome supplies (Ctrl + Shift + I) and then in the Styles tab on the right scorll down until you see the measurements. It really helps understand the CSS box model.
Did you add 'px' here..
$('body').css('margin-right', scrollbarwidth+'px')??
Just nowI tried in w3schools. If you add 'px' to above syntax, it is working for me.