Ok so here's a challenge: I'm looking to rotate a fixed element when you scroll up and down inside a < div > - and not when you scroll on the entire page. So how do i target the scroll within a specific < div> (my div has classname="elementor")?
My code so far looks like this:
HTML
/* The image i'm trying to rotate */
<img class="portfolio" id="rotatelogo" src="http://jakobnatorp.com/wp-content/uploads/2021/10/cropped-JAKOB-LERCHE-DAA-NATORP.png"/>
/* And a div container with class="elementor" */
CSS
.portfolio {
position: fixed;
width:150px;
height:150px;
margin-top:50px;
margin-bottom:-300px;
margin-left:50px;
}
.elementor {
width: 100vh;
height: 100vw;
overflow-x: scroll;
overflow-y: scroll;
transform: rotate(-90deg) translateX(-100vh);
transform-origin: top left;
-ms-overflow-style: none;
}
JS
var element = document.getElementsByClassName("elementor")[0]
var elem = document.getElementById("rotatelogo");
element.addEventListener('scroll', function() {
var value = element.scrollY * 0.25;
elem.style.transform = `translatex(-50%) translatey(-50%) rotate(${value}deg)`;
});
Edit: I changed the code and it works now. I replaced the "scrollY" with "scrollTop". My new JS looks like this:
var element = document.getElementsByClassName("elementor")[0]
var elem = document.getElementById("rotatelogo");
element.addEventListener('scroll', function() {
var value = element.scrollTop * 0.25;
elem.style.transform = `translatex(-50%) translatey(-50%) rotate(${value}deg)`;
});
If I understand it right, you could select the element you'r adding an event to.
Something like :
const scrollDiv = document.querySelector(".scrollOnMe");
scrollDiv.addEventListener("wheel", () => {
console.log("Scrolling !");
})
div {
height: 30px;
}
.scrollOnMe {
background-color: green;
}
.foo {
background-color: red;
}
<div class="scrollOnMe">Scroll on me !</div>
<div class="foo">Don't :(<div>
Related
I have this piece of css but I want to change the width in the keyframe with a variable in javascript. How can I do that?
#keyframes test {
100% {
width: 100%;
}
}
Does it have to be a keyframe animation? Typically you would use the CSS transition property for this kind of animation powered by JavaScript, like this:
var width = 50;
document.getElementById('button').addEventListener('click', () => {
width += 50;
document.getElementById('box').style.width = `${width}px`;
});
#box {
background: red;
height: 50px;
width: 50px;
transition: width .5s;
margin-bottom: 1em;
}
<div id="box"></div>
<button id="button">Change Width</button>
If you have a more general animation (that can't be encompassed by just doing a transition) then you can use JS to set a CSS variable.
Taking the example in the question, replace the 100% with a variable:
#keyframes test {
100% {
width: var(--bg);
}
}
and the Javascript you'd have something like:
thediv.style.setProperty('--bg', '60px');
#JohnUleis already answeared correctly. I was too late. But I add just for fun a solution. Is named: How lfar is Rom? ;-)
Cheers
let root = document.documentElement;
const div = document.querySelector('div');
root.addEventListener("mousemove", e => {
div.style.setProperty('--width', e.clientX + "px");
div.innerHTML = e.clientX + ' km';
});
:root {
--width: 100%;
}
div {
background: hotpink;
width: var(--width);
text-align: center;
color: white;
}
<div>how far is rom?</div>
I want to make an image zoom effect on my project. When the user clicks on that image I want image to be zoom. But the problem is that the div which I create dynamically is appended top of the body what I want is append this div above that image.
I try something like this:-
export const imagezoom = () => {
const image = document.querySelectorAll(".zoom");
image.forEach((e) => {
e.addEventListener("click", zoom);
});
};
function zoom(event) {
const div = document.createElement("div");
const container = document.querySelector("body");
div.classList.add("zoomDiv");
const img = document.createElement("img");
img.src = this.src;
img.classList.add("zoomImage");
console.log("added");
div.appendChild(img);
container.appendChild(div);
}
.zoomDiv {
position: absolute;
top: 100%;
left: 0;
width: 100%;
height: 100%;
z-index: 11;
background-color: black;
transition: all 250ms ease-in;
display: flex;
justify-content: center;
align-items: center;
animation-name: zoom;
animation-duration: 0.6s;
}
.zoomImage {
width: 80%;
height: 80%;
z-index: 11;
animation-name: zoom;
animation-duration: 0.6s;
object-fit: contain;
}
#keyframes zoom {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<img class="zoom" src="../../../assets/Subject/physics/8.png" alt="" />
Here's what you do.
Put the image in a div. Make sure the image uses all the space in the containing div.
Set the position of that div to "relative".
Use
var x = evt.pageX - $('#element').offset().left;
var y = evt.pageY - $('#element').offset().top;
To get the position of the mouse relative to the div container you put your image in. Make sure the dynamically generated image position to "absolute".
Use x and y to set the left and top CSS properties dynamically.
I don't probably think I understood what you are trying to do but according to this title How to add dynamic div on that position where i clicked try the following.
Add some JavaScript on your clickable image where you want to zoom
Example
var x = document.querySelector('clickable_image');
x.addEventListener('click', e => {
image.style.setProperty('--x',e.clientX + 'px');
image.style.setProperty('--y',e.clientY + 'px');
});
and if you already done to set the above properties ad them in your CSS first of all add position: absolute; and add the following where --y is for top and --x for left
.clickable_image {
position: absolute;
top: var(--y);
left: var(--x);
}
the above will positioning that div to where you clicked
The problem with my slider is that when it gets to the last slide and i click next it jumps over the two slides to get to the first one. Similarly when i am on the first slide and click previous, it jumps over slides to get to the last one. I would like to make it that when i get to the last slide and click NEXT the first slide would come from the right to left. (similar concept for the PREVIOUS button on first slide). I tried using insertBefore() and appendChild() for the slides but couldn't figure it out...
Here is my code:
// Slider
const slider_wrapp = document.querySelector('.tract-slider');
const slider = document.querySelector('.tract-slider-wrapp');
var slide = document.getElementsByClassName('tract-slide');
const leftBtn = document.querySelector('.slide-left');
const rightBtn = document.querySelector('.slide-right');
let swWidth = slider_wrapp.clientWidth;
let sliderWidth = swWidth * slide.length;
let slideWidth = 0;
slider.style.width = sliderWidth + "px";
for (var i = 0; i < slide.length; i++) {
slide.item(i).style.width = swWidth + "px";
}
function moveRight() {
slideWidth === sliderWidth - swWidth ? slideWidth = 0 : slideWidth += swWidth;
slider.style.transform = "translateX(" + (-slideWidth) + "px)";
}
function moveLeft() {
slideWidth === 0 ? slideWidth = sliderWidth - swWidth : slideWidth -= swWidth;
slider.style.transform = "translateX(" + (-slideWidth) + "px)";
}
rightBtn.addEventListener("click", function() {
moveRight();
});
leftBtn.addEventListener("click", function() {
moveLeft();
});
.tract-slider {
width: 100%;
height: 75vh;
position: relative;
overflow: hidden;
}
.tract-slider-wrapp {
height: 100%;
position: relative;
-webkit-transition: all 350ms cubic-bezier(.08, .13, 0, .81);
-o-transition: all 350ms cubic-bezier(.08, .13, 0, .81);
transition: all 350ms cubic-bezier(.08, .13, 0, .81);
}
.tract-slide {
height: 100%;
float: left;
position: relative;
display: block;
background-position: center;
-webkit-background-size: cover;
background-size: cover;
}
.tract-slide:nth-child(1) {
background-image: url("https://static.pexels.com/photos/126282/pexels-photo-126282.jpeg");
}
.tract-slide:nth-child(2) {
background-image: url("https://static.pexels.com/photos/29017/pexels-photo-29017.jpg");
}
.tract-slide:nth-child(3) {
background-image: url("https://static.pexels.com/photos/70760/dandelion-dandelion-seeds-taraxacum-fluffy-70760.jpeg");
}
.tract-slider-control {
position: absolute;
left: 0;
bottom: 0;
background: #ffffff;
padding: 1em;
}
.tract-slider-btn {
display: inline-block;
cursor: pointer;
margin-left: 1em;
}
.tract-slider-btn:nth-child(1) {
margin-left: 0;
}
<div class="tract-slider">
<div class="tract-slider-wrapp">
<div class="tract-slide"></div>
<div class="tract-slide"></div>
<div class="tract-slide"></div>
</div>
<div class="tract-slider-control">
<div class="tract-slider-btn slide-left">Prev</div>
<div class="tract-slider-btn slide-right">Next</div>
</div>
</div>
PS. Please use JavaScript for solution
Creating an infinite slider means you need to move your slides around in DOM so they give the impression of a continuous track.
The first thing you need to change is having their backgrounds tied up to their position in DOM. If we want to slide back from first slide to the last one, we need to take the last slide, prepend it before the first one but, considering your current CSS, that will change the backgrounds of all slides, as they are currently bound to their position in DOM (...:nth-child {background-image:...}...).
The second thing that needs changing is positioning the slides into the slider track. If they're floated, whenever we change their order, all the rest of the slides will be affected. By positioning them with position:absolute each slide moves independently, without affecting the others, so it's easier to rearrange them while keeping control.
Long story short, I started from scratch and placed all methods inside a single object: theSlider.
The reset() function does the heavy lifting: it puts before class on first element, current on second and after on all the rest. So you have to put the "last" slide first, because the slider will start with it appended before the "current" one.
The sliding is done by applying go-left and go-right classes to the track. After the transition is done, I just move the first/last slide into the new position, depending on case, and run reset() again (which strips all classes and reapplies them based on new positions).
Animations are handled by CSS. All JavaScript does is apply/remove classes and move the slides in DOM.
var theSlider = {
track : document.querySelector('.tract-slider-wrapp'),
// has to match `transition-duration` in CSS:
duration : 600,
reset : function() {
var slides = document.querySelectorAll('.tract-slider-wrapp > div');
for (var i = 0; i < slides.length; i++) {
slides[i].className = '';
slides[i].classList.add(i > 1? 'after' : (i ? 'current':'before'))
}
},
init : function() {
theSlider.reset();
theSlider.track.classList.remove('not-loaded')
},
next : function() {
theSlider.track.classList.add('go-right');
setTimeout(function(){
var firstSlide = document.querySelector('.tract-slider-wrapp > div:first-child');
theSlider.track.appendChild(firstSlide);
theSlider.reset();
theSlider.track.classList.remove('go-right')
},theSlider.duration)
},
prev : function() {
theSlider.track.classList.add('go-left');
setTimeout(function() {
var lastSlide = document.querySelector('.tract-slider-wrapp > div:last-child');
theSlider.track.insertBefore(lastSlide, theSlider.track.firstChild);
theSlider.reset();
theSlider.track.classList.remove('go-left')
},theSlider.duration)
},
prevButton : document.querySelector('.slide-left'),
nextButton : document.querySelector('.slide-right')
};
window.addEventListener("load", theSlider.init);
theSlider.prevButton.addEventListener('click', theSlider.prev);
theSlider.nextButton.addEventListener('click', theSlider.next);
.tract-slider {
width: 100%;
height: 75vh;
position: relative;
overflow: hidden;
background-color: #f5f5f5;
}
.tract-slider-wrapp {
height: 100%;
transition: all 350ms cubic-bezier(.08, .13, 0, .81);
opacity: 1;
}
.tract-slider-wrapp.not-loaded {
opacity: 0;
}
.tract-slider-wrapp>div {
height: 100%;
position: absolute;
background: transparent no-repeat 50% 50% /cover;
width: 100%;
}
.tract-slider-wrapp > div.before {
margin-left: -100%;
}
.tract-slider-wrapp > div.current + div {
margin-left: 100%;
}
.tract-slider-wrapp > div.after ~ div {
opacity: 0;
}
.tract-slider-control {
position: absolute;
width: 100%;
z-index: 1;
top: 50%;
display: flex;
justify-content: space-between;
transform: translateY(-50%);
}
.tract-slider-control div {
background-color: rgba(0,0,0,.35);
padding: .5rem 1rem;
color: white;
cursor: pointer;
}
.tract-slider-control :first-child {
border-radius: 0 17px 17px 0;
}
.tract-slider-control :last-child {
border-radius: 17px 0 0 17px;
}
body {
margin: 0;
}
.go-right div {
transform: translateX(-100%);
}
.go-left div {
transform: translateX(100%);
}
.go-right div, .go-left div {
transition-property: transform;
transition-timing-function: cubic-bezier(.4,0,.2,1);
/* has to match `duration` in js: */
transition-duration: 600ms;
}
<div class="tract-slider">
<div class="tract-slider-wrapp not-loaded">
<div style="background-image:url('https://static.pexels.com/photos/126282/pexels-photo-126282.jpeg')"></div>
<div style="background-image:url('https://static.pexels.com/photos/29017/pexels-photo-29017.jpg')"></div>
<div style="background-image:url('https://static.pexels.com/photos/70760/dandelion-dandelion-seeds-taraxacum-fluffy-70760.jpeg')"></div>
</div>
<div class="tract-slider-control">
<div class="tract-slider-btn slide-left">Prev</div>
<div class="tract-slider-btn slide-right">Next</div>
</div>
</div>
If you want to change the animation duration you need to change it in both js and css.
The only current limitation is it needs at least 3 slides to work. I guess it could be adjusted to work with only two slides by: cloning the "inactive" slide into third position, removing the clone after transition and cloning the other one.
ToDo's:
prefix CSS so it works in more browsers
replace .classList.add('whatever') with .className += ' whatever' and
.classList.remove('whatever') with .className.replace('whatever', '') if you want to show IE some love.
I told the above just to tell you this: if you want to get going, don't reinvent the wheel.
It's great you use vanilla javascript. But sooner or later you'll end up writing your own wrappers for common things. Depending on how good you are/have become, you'll write your own, limited, custom version of jQuery. Allow me to put things into perspective: Google included a lite version of jQuery into AngularJS. It's that good.
You, as an single developer, do not stand a chance at writing a better, more streamlined and tested version of it. And besides, you don't have to. Use your skill and abilities to go forward, not sideways.
I'm trying to change the background image of an element after a certain position has been scrolled past. Here's a snippet of my code:
<body>
<script>
window.onscroll = function() {scrollBG()};
function scrollBG() {
if (document.body.scrollTop > document.getElementById("one").getBoundingClientRect().top ||
document.documentElement.scrollTop > document.getElementById("one").getBoundingClientRect().top) {
document.getElementById("outer").style.backgroundImage = "url('imgs/pic1.jng')";
} else {
document.getElementById("outer").style.backgroundImage = "url('imgs/pic2.jpg')";
}
}
</script>
<table id="outer">
I'm using a similar coding style to show/hide a "back to top" button after a certain scroll position that functions just fine. I don't think there's a conflict between the two (though inline scripting isn't my preferred style) because even when I remove everything related to the "back to top" button, my code still fails to function.
Is this a stupid syntactical error, or is there a more fundamental error to my approach?
I've tweaked your code a little and it's working:
jsFiddle 1
var divOuter = document.getElementById("outer"),
divOne = document.getElementById("one");
window.onscroll = function() {scrollBG()};
function scrollBG() {
var oneTop = divOne.getBoundingClientRect().top;
if(document.body.scrollTop > oneTop ||
document.documentElement.scrollTop > oneTop){
divOuter.style.backgroundImage = "url('//www.planwallpaper.com/static/images/518071-background-hd_xO1TwRc.jpg')";
} else {
divOuter.style.backgroundImage = "url('//www.planwallpaper.com/static/images/maxresdefault_YodSsVN.jpg')";
}
}
body { margin: 0; padding: 0; height: 1500px; }
#outer {
position: fixed;
background: url('//www.planwallpaper.com/static/images/maxresdefault_YodSsVN.jpg') no-repeat;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#one {
width: 100%;
height: 150px;
background-color: orange;
color: white;
position: relative;
top: 400px;
}
<div id="outer"></div>
<div id="one"><h1>This is One</h1></div>
However, following the above method will be inefficient as IMHO it makes an extra HTTP request for the images every time the scroll goes up and down the threshold and thus you'll see flickering every time the background images get changed.
So it'd be better if we make use of an external CSS class, i.e #outer.bg2, and just add/remove it depending on the position of the scroll and this will fetch a cached version of the image which makes it smooth [except for the first time when the image is being requested for the first time]. Like below:
jsFiddle 2
var divOuter = document.getElementById("outer"),
divOne = document.getElementById("one");
window.onscroll = function() { scrollBG() };
function scrollBG() {
var oneTop = divOne.offsetTop;
if (document.body.scrollTop > oneTop ||
document.documentElement.scrollTop > oneTop) {
divOuter.classList.add('bg2');
} else {
divOuter.classList.remove('bg2');
}
}
body{ margin:0; padding:0; height: 1500px; }
#outer{
position:fixed;
background: url('//www.planwallpaper.com/static/images/maxresdefault_YodSsVN.jpg') no-repeat;
top:0;
left:0;
right:0;
bottom:0;
}
#outer.bg2{
background: url('//www.planwallpaper.com/static/images/518071-background-hd_xO1TwRc.jpg');
}
#one{
width:100%;
height:150px;
background-color:orange;
color:white;
position:relative;
top:400px;
}
<div id="outer"></div>
<div id="one"><h1>This is One</h1></div>
In the above code the triggering will happen when the scroll meets the bottom of the element #one, if you want the triggering to happen according to the top edge then replace this:
var oneTop = divOne.offsetTop;
With this:
var oneTop = divOne.offsetTop - divOne.offsetHeight;
jsFiddle 3
Apologies for the long winded question but any help would be much appreciated!
I have a navigation div on a website that disappears when the screen gets smaller to be replaced by a menu button, using a media query. The menu button uses JavaScript to show and hide the menu.
This all works apart from one small bug that I can't figure out, it's a bit hard to explain so I'll bullet point it -
1) Open small browser window so button shows.
2) Open and close menu using button.
3) Maximise screen.
4) The button disappears (which it should) but the menu doesn't reappear.
You can see a live example here - http://andrewbruce.me
I'll put relevant code below -
var clicks = 0;
function decide(x) {
if (clicks == 0) {
document.getElementById("nav").style.visibility = "visible";
document.getElementById("nav").style.opacity = "1";
x.classList.toggle("change");
clicks = 1;
}
else if (clicks == 1) {
document.getElementById("nav").style.visibility = "hidden";
document.getElementById("nav").style.opacity = "0";
x.classList.toggle("change");
clicks = 0;
}
}
#nav {
height: 100%;
width: 22%;
text-align: center;
box-shadow: 2px 2px 5px #888888;
visibility: visible;
opacity: 1;
transition: all .3s ease-in-out;
background-color: #1b1d1f;
float: left;
position: fixed;
z-index: 2;}
#media handheld, screen and (max-width: 1000px) {
#nav {width: 40%; visibility: hidden; opacity: 0;}
.menuButton {visibility: visible;}
}
<div class="menuButton" onclick="decide(this);">
<div id = "bar1"></div>
<div id = "bar2"></div>
<div id = "bar3"></div>
</div>
Try this.
I hope helps.
#media (min-width: 1000px){
#nav{
opacity:1!important;
visibility: visible!important;
}
}
You should not change style by this method document.getElementById("nav").style, it will add inline style and override your properties. Instead create a class with those properties, then use scripts to toggle it.
For example:
CSS
.nav-hidden {
visibility: hidden;
opacity: 0;
}
JS
element.classList.add("nav-hidden");
element.classList.remove("nav-hidden");
use below JS
window.addEventListener("resize", menuChange);
function menuChange() {
if (window.innerWidth > 999){
document.getElementById("nav").style.visibility = "visible";
document.getElementById("nav").style.opacity = "1";
x.classList.toggle("change");
}
}