document.onscroll = function()
{
if (document.documentElement.scrollTop + window.innerHeight > document.documentElement.scrollHeight * 0.75)
{
var height = document.documentElement.height;
document.documentElement.height = height + 500;
alert('75%');
}
};
html {
height: 1000px;
}
I want this code to run every time the scrollbar reaches 75% of its height to add 500px then repeat the function when it reaches the 75% again.
The code works on my environment but if I scrolled fast I get multiple alerts and the 500px gets added more than once.
I tried the following:
document.documentElement.scrollTop + window.innerHeight == document.documentElement.scrollHeight * 0.75
but it never alerted.
There is no such height property on documentElement object. To update the height you have to access style property documentElement.
Try this.
document.onscroll = function()
{
if (document.documentElement.scrollTop + window.innerHeight > document.documentElement.scrollHeight * 0.75)
{
var height = document.documentElement.style.height;
document.documentElement.style.height = (+height.slice(0,-2) + 500) + "px";
alert('75%');
}
};
<!DOCTYPE html>
<html style="height: 1600px">
</html>
Hope this helps :).
EDIT: Attached screenshots.
Use console for better results
var sw_75 = false;
window.onscroll = function () {
if (sw_75) return;
if (document.documentElement.scrollTop + window.innerHeight > document.documentElement.scrollHeight * 0.75) {
console.log('75%');
sw_75 = true;
var body = document.getElementsByTagName('body')[0];
var height = parseInt(body.style.height.replace("px", ""));
body.style.height = (height + 500) + "px";
sw_75 = false;
}
};
html, body {
margin: 0px;
padding: 0px;
}
<body style="height:1000px;"></body>
Related
I want to make an image randomly show up every 2 sec using jQuery. But the image always shows in a static position (top-left). I console.logged the attribute of the image and its left and top shows random px.
let score = 0;
let id = 0;
$(document).ready(function () {
setInterval(setImage, 2000);
});
function setImage() {
$("#container").append(
`<img src='images/virus.gif' id='${id}' width='75px' position='absolute' class='virus' left=${randomLeft()} top=${randomTop()}/>`
);
$("#" + id)
.slideUp(0)
.fadeIn(1000);
id++;
$("#" + id).on("click", function (event) {
$(event.target).remove();
score++;
console.log($(event.target).attr("id"), score);
});
}
function randomTop() {
let height = $(window).height();
let randomHeight = Math.floor(Math.random() * (height - 75)) + "px";
return randomHeight;
}
function randomLeft() {
let width = $(window).width();
let randomLeft = Math.floor(Math.random() * (width - 75)) + "px";
return randomLeft;
}
You need to use style rather than left/top attributes
style="left:10px;right:10px"
Couple of other tweaks to your snippet:
include jquery (option on the left of snippet editor adds <script> to the html)
needs a #container to put them in, I made this position:relative so the position:absolute will work
removed the need for the $("#"+id) re-finding by chaining the append
put the i++ at the beginning so that you don't try to attach click to something that doesn't exist yet (but doesn't matter as not used for re-finding new element)
$(this).remove() must come after $(this).attr("id") - you were removing it before getting its ID
used this inside the click handler rather than event.target
would be better as data-id=${++id} rather than using a numerical id, but that's a small change (not made)
Updated snippet:
let score = 0;
let id = 0;
$(document).ready(function() {
setInterval(setImage, 2000);
});
function setImage() {
var img = $(`<img src='images/img4.jpg' id='${++id}' class='virus' style='left:${randomLeft()};top:${randomTop()}' />`);
img.appendTo("#container")
.slideUp(0)
.fadeIn(1000)
.on("click", function() {
score++;
console.log($(this).attr("id"), score);
$(this).remove();
});
}
function randomTop() {
let height = $(window).height();
let randomHeight = Math.floor(Math.random() * (height - 75)) + "px";
return randomHeight;
}
function randomLeft() {
let width = $(window).width();
let randomLeft = Math.floor(Math.random() * (width - 75)) + "px";
return randomLeft;
}
#container {
position: relative;
height: 100%;
width: 100%;
}
.virus {
border: 1px solid red;
position: absolute;
width: 75px;
height: 20px;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='container'></div>
I am using clipping paths to change my logo colour base on the background colour.
In addition to this the logo scrolls from top to bottom based on the users vertical position on the page. Top of page = logo at top, bottom of page = logo at bottom etc.
Unfortunately when I added the clipping paths the logos lost their scroll position and after the first one, do not work at all.
Is there a way around this? Also, the logo position was a little off to start with so if there is any way of addressing this at the same time.
You can see the original question here:
div position based on scroll position
I have tried this, but I can't seem to get it to work.
Scroll position lost when hiding div
I am using Advanced Custom Fields and each sections PHP file has this in the header as part of the clipping path using either the white or dark version of the logo accordingly. Its parent is positioned relatively and its child absolutely.
div class="logo-scroll">
<div class="scroll-text">
<img width="53px" height="260px" src="/wp-content/uploads/2019/07/sheree-walker-web-design-edinburgh-vertical-01.svg"/>
</div>
</div>
The Javascript
const docHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
const logo = document.querySelector('.scroll-text');
const logoHeight = logo.offsetHeight;
// to get the pseudoelement's '#page::before' top we use getComputedStyle method
const barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
let viewportHeight, barHeight, maxScrollDist, currentScrollPos, scrollFraction;
logo.style.top = barTopMargin + 'px';
window.addEventListener('load', update);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
setSizes();
function update() {
currentScrollPos = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
scrollFraction = currentScrollPos / (docHeight - viewportHeight);
logo.style.top = barTopMargin + (scrollFraction * maxScrollDist) + 'px';
}
function setSizes() {
viewportHeight = window.innerHeight;
// to get the pseudoelement's '#page::before' height we use getComputedStyle method
barHeight = parseInt(getComputedStyle(document.querySelector('#page'), '::before').height);
maxScrollDist = barHeight - logoHeight;
update();
}
The CSS
.logo-scroll .scroll-text img {
padding: 0 6px 0 17px;
}
#page::before {
content: "";
position: fixed;
top: 30px;
bottom: 30px;
left: 30px;
right: 30px;
border: 2px solid white;
pointer-events: none;
-webkit-transition: all 2s; /* Safari prior 6.1 */
transition: all 2s;
}
.logo-scroll {
position: fixed;
left: 30px;
top: 30px;
bottom: 30px;
border-right: 2px solid white;
width: 75px;
z-index: 10;
}
.scroll-text {
position: fixed;
}
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = document.documentElement.scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = document.documentElement.scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
updated and tested.
to check it put this code in your console:
document.removeEventListener('scroll', update);
document.onscroll = function() {
let _logoHeight = logos[0].offsetHeight;
let _barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
let _viewportHeight = window.innerHeight;
let _maxScrollDist = document.documentElement.scrollHeight - _viewportHeight;
let currentScrollPos = document.documentElement.scrollTop;
let percent100 = currentScrollPos + _viewportHeight;
let scrolledPercent = currentScrollPos * 100/_maxScrollDist;
let newTop = ((_viewportHeight - _logoHeight/2)*scrolledPercent/100);
let middle = currentScrollPos + _viewportHeight/2;
let middleY = _maxScrollDist/2; // 100
if (middle >= (_maxScrollDist+_viewportHeight)/2) {
let y1 = middleY - Math.floor(middle - (_maxScrollDist+_viewportHeight)/2);
let p = y1*100/middleY;
newTop = _viewportHeight/2 - _logoHeight/2;
newTop += (100-p)*(_viewportHeight/2)/100;
newTop -= (100-p)*(30 +_logoHeight/2)/100;
newTop = Math.max(newTop, _viewportHeight/2 - _logoHeight/2); /*fix*/
} else {
let y2 = middleY - Math.floor(-middle + (_maxScrollDist+_viewportHeight)/2);
let p = y2*100/middleY;
newTop = 30*(100-p)/100+(_viewportHeight/2 - (_logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, _viewportHeight/2 - _logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
CSS fix:
custom.css :: line 767
#media (max-width: 1000px)...
.scroll-text {
padding-left: 13px;
/*width: 27px;*/
}
.scroll-text img {
/* remove it. but if necessary move it to .scroll-text rule above
width: 27px; */
}
custom.css :: line 839
#media (max-width: 599px)...
.logo-scroll {
/*display: none; why! remove it*/
}
custom.css :: line 268
.scroll-text {
position: fixed;
/* height: 280px; remove it*/
padding-left: 20px;
}
see this capture
finally, have a nice day and goodby.
You are selecting only the first 'logo-text'. Instead of:
const logo = document.querySelector('.scroll-text');
You should use querySelectorAll:
const logos = document.querySelectorAll('.scroll-text');
Then, in your scroll handler, you should move them all.
So, you would then substitute each instance of usage of logo with a loop through the all the logo elements:
logos.forEach(logo => logo.style.top = ...);
Please be aware that you are doing quite expensive stuff in a scroll handler, which is not great for rendering performance. You might also want to use requestAnimationFrame to improve the rendering performance. Check out the reference page on MDN. Actually, I quickly whipped a version using requestAnimationFrame but there was no sensible performance improvement. This is probably due to the fact that apparently rAF fires roughly at the same rate than the scroll event. Anyway, I removed it to avoid confusion. If you detect performance issues, let me know.
I suggest, though, that you move the logo using transform: translate() rather than top. Here you have a complete solution. I tried it in Chrome.
const docHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
const logos = document.querySelectorAll('.scroll-text');
const logoHeight = logos[0].offsetHeight;
// to get the pseudoelement's '#page::before' top we use getComputedStyle method
const barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
let viewportHeight, barHeight, maxScrollDist, currentScrollPos, scrollFraction;
window.addEventListener('load', update);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
setSizes();
function update() {
currentScrollPos = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
scrollFraction = currentScrollPos / (docHeight - viewportHeight);
const translateDelta = barTopMargin + (scrollFraction * maxScrollDist);
logos.forEach(logo => logo.style.transform = `translateY(${translateDelta}px)`);
}
function setSizes() {
viewportHeight = window.innerHeight;
// to get the pseudoelement's '#page::before' height we use getComputedStyle method
barHeight = parseInt(getComputedStyle(document.querySelector('#page'), '::before').height);
maxScrollDist = barHeight - logoHeight;
update();
}
I am trying to animate an opacity value from 0 to 1, based on the scroll position within the viewport height. The code below sets variables for windowHeight and scrollTop, which can be combined to calculate percentageScrolled (0–100) of the viewport height. Based on this I am able to switch CSS values at set points, but instead I want to gradually change the opacity from 0–100 of percentageScrolled.
How can I adjust the code below to transition/animate the opacity?
Thanks.
$(window).on('scroll', function(){
// Vars
var windowHeight = $(window).height();
var scrollTop = $(this).scrollTop();
var percentageScrolled = (scrollTop*100)/windowHeight;
if( percentageScrolled < 100 ) {
$('.colour-overlay').css('opacity', '1');
} else {
$('.colour-overlay').css('opacity', '0');
}
});
You can remove the if and set the opacity to the percentage divided by 100
$(window).on('scroll', function() {
// Vars
var windowHeight = $(window).height();
var scrollTop = $(this).scrollTop();
$('.colour-overlay').css('opacity', scrollTop / windowHeight);
});
.colour-overlay {
display: block;
width: 20px;
height: 1200px;
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="colour-overlay"></div>
$(‘.colour-overlay’).css(opacity, percentageScrolled / 100);
Instead of if else statement.
Also as a general advice try to avoid using var, use const or let instead and if your project doesnt depend on jquery try to avoid it too.
const overlays = document.querySelectorAll(‘.colour-overlay’);
window.addEventListener('scroll', () => {
const windowHeight = window.offsetHeight;
const scrollTop = window.scrollTop;
const percentageScrolled = (scrollTop * 100) / windowHeight;
for (const overlay of overlays) {
overlay.style.opacity = percentageScrolled / 100;
}
});
This would be the pure js solution.
Dont know if i understood you right, but a wrote an example have a look.
$(document).on('scroll', function(){
// Vars
// use body instead of window, body will return the right height where window will return the view size
var windowHeight = $("body").height();
var scrollTop = $(this).scrollTop();
var percentageScrolled = Math.abs((((scrollTop / windowHeight) * 100) / 100 ));
$('.colour-overlay').css('opacity', percentageScrolled);
});
.colour-overlay{
background:red;
height:1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="colour-overlay"></div>
So I have this jQuery function that adds / removes a CSS class to an element after 600px of the viewport height has been scrolled.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 600) {
$(".cta_box").addClass('fadeout');
} else {
$(".cta_box").removeClass('fadeout');
}
});
I'd like to tweak it so instead of working based off a pixel value, it works off of the view height css measurement "VH", but the equivalent results are what matter in this case.
It can be done by getting the window height using $(window).height().
For instance suppose you have to scroll half the screen more (height is 150vh) and you have to detect when 40% has been scrolled:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 0.4 * $(window).height()) {
$(".cta_box").addClass('fadeout');
} else {
$(".cta_box").removeClass('fadeout');
}
});
body{
margin: 0;
height: 150vh;
}
.cta_box {
height: 100%;
background: green;
}
.cta_box.fadeout {
background: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="cta_box"></div>
Use a percentage of the window height to compare
$(window).scroll(function() {
var height = $(window).height(),
scroll = $(window).scrollTop()
limit = 0.6; //implies 60 vh or 60% of viewport height
if (scroll >= height*limit) {
$(".clearHeader").addClass("darkHeader");
} else {
$(".clearHeader").removeClass("darkHeader");
}
});
and even better would be to update some variable only when the window is resized to avoid computations all the time
var limit = 0.6, //implies 60 vh or 60% of viewport height
scrollLimit = 0;
$(window).resize(function(){
scrollLimit = $(window).height() * limit;
}).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= scrollLimit ) {
$(".clearHeader").addClass("darkHeader");
} else {
$(".clearHeader").removeClass("darkHeader");
}
}).trigger('resize').trigger('scroll'); // trigger both events on start to force initial setup
Try something like this
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
$(".clearHeader").addClass("darkHeader");
} else {
$(".clearHeader").removeClass("darkHeader");
}
});
For retrieveing the viewport Height, you could use $(window).innerHeight():
$(window).scroll(function() {
var scroll = $(window).innerHeight();
if (scroll >= 600) {
$(".cta_box").addClass('fadeout');
} else {
$(".cta_box").removeClass('fadeout');
}
});
Hope this helps.
Leo.
I am practicing my vanilla javascript, by writing an image gallery application.
The function I am working on is the swap from my imageRoll to my mainImage.
The swap works fine, and the height/width calculation works fine as well... but when I tell it to calculate the margin of the parent element and the new image, it fails unless you click the source image a second time.
JSFiddle: https://jsfiddle.net/un64w9hh/2/
Here's the function code:
function galleryToMainImg() {
var gNumList = this.parentNode.classList;
var gNum = gNumList[0].slice(1,2);
var imgId = "g" + gNum + "MainImage";
var imgName = document.getElementById(imgId);
var height = this.offsetHeight;
var width = this.offsetWidth
var styles;
var ratio;
imgName.src = this.src;
if (height > width) {
ratio = (width / height) * 100;
styles = "height:100%; margin: 0px " + ((imgName.parentNode.offsetWidth - imgName.offsetWidth) / 2) + "px;";
} else if (width > height){
ratio = (height / width) * 100;
styles = "width:100%; margin: " + ((imgName.parentNode.offsetHeight - imgName.offsetHeight) / 2) + "px 0px;";
} else {
ratio = 1;
styles = "width:100%; margin: " + ((imgName.parentNode.offsetHeight - imgName.offsetHeight) / 2) + "px 0px;";
}
imgName.style = styles;
}
Interestingly enough, as long as the images clicked from the gallery are the same layout (portrait vs landscape) it works every time after the first one. But if you switch layout, it breaks every time.
First click:
<img id="g1MainImage" src="./images/image01.png" alt="Caption" style="height: 100%; margin: 0px;">
Second click:
<img id="g1MainImage" src="http://ne.fario.us/designs/ImageGallery/images/image01.png" alt="Caption" style="height: 100%; margin: 0px 9px;">
I'm just not sure what is going on. Any help would be greatly appreciated.
I have been playing with your code for quite a while, your function galleryToMainImg() is just fine, ratio and .offsetWidth properties OK. I have eliminated every margin property from your .css and error persists.
Leaving calculations on the side, this works decently (no error, but I still have no margin properties applied by .css):
function galleryToMainImg() {
gNumList = this.parentNode.classList;
gNum = gNumList[0].slice(1,2);
imgId = "g" + gNum + "MainImage";
imgName = document.getElementById(imgId);
height = this.offsetHeight;
width = this.offsetWidth;
styles='';
ratio='';
imgName.src = this.src;
if (height > width) {
ratio = (width / height) * 100;
styles = 'height:100%; margin-left: 38%; margin-top: 0%'; /*+((imgName.parentNode.offsetWidth - imgName.offsetWidth) / 2)*/
} else if (width > height){
ratio = (height / width) * 100;
styles = "width:100%; margin-left: 7%; margin-top: 0%";/*+ ((imgName.parentNode.offsetHeight - imgName.offsetHeight) / 2) + "px;"*/
} /*else {
ratio = 1;
styles = 'width:100%; margin: "' + ((imgName.parentNode.offsetHeight - imgName.offsetHeight) / 2) + 'px;"';
}*/
imgName.style = styles;
}
Hope it is helpful for future examination.
that's normal because you change the image size on the click. I updated your fiddle to see the alerts. See hier jsfiddle.net/un64w9hh/4/