Javascript - set onclick to only first section of page - javascript

I am developing this site: https://studioboom.superhi.com/
Is it possible to apply the image click to only the first section, so once scrolled you are able to click on links rather than add an image.
JS:
const images = [
'benjones_flip1.jpg',
'benjones_home1.jpg',
'ben_jones_ts2.jpg',
'benjones_gs1.jpg',
'benjones_jt1.jpg',
'benjones_dlf4.jpg'
]
let i = 0
function placeImage(x, y) {
const nextImage = images[i]
const img = document.createElement('img')
img.classList.add('external-loaded-img')
img.setAttribute('src', nextImage)
img.style.left = x + 'px'
img.style.top = y + 'px'
document.body.appendChild(img)
i = i + 1
if (i >= images.length) {
i = 0
}
}
document.addEventListener('click', function(event) {
event.preventDefault()
placeImage(event.pageX, event.pageY)
})
document.addEventListener('touchend', function(event) {
event.preventDefault()
placeImage(event.pageX, event.pageY)
})
.external-loaded-img {
position: absolute;
transform: translate(-50%, -50%) scale(0.5);
animation: fadein 0.5s;
z-index: 10;
overflow: hidden;
}
#keyframes fadein {
0% {opacity: 0;}
100% {opacity: 1;}
}

Either wait to set up your event handlers until the container of the elements you want them to apply to are present in the page, and then hook up the handlers on that container:
container.addEventListner('click', /*...*/);
container.addEventListner('touchend', /*...*/);
...or keep them hooked up on document, but check when the event occurs that it passed through that container ("event delegation"):
document.addEventListner('click', function(event) {
if (!event.target.closest("selector-for-the-container")) {
return;
}
// ...
});
document.addEventListner('touchend', /*...*/);
if (!event.target.closest("selector-for-the-container")) {
return;
}
// ...
});
More on closest on MDN, including information about polyfills if needed.

document.querySelector(".about").addEventListener('click', function(event) {
event.preventDefault()
placeImage(event.pageX, event.pageY)
})

Something like this does work, if I am clicking on section.photo1, if I clicking on an actual image, it doesn't. Is there a work around for this?
document.addEventListener('click', function(event) {
if (!event.target.closest("section.photo1")) {
return;
}
event.preventDefault()
placeImage(event.pageX, event.pageY)
})

Related

Trying to have images only below viewport fade in on scroll

I'm trying to follow this tutorial for my portfolio website.
I've almost got it working but instead of only fading in images as they come into the window, it is fading the images that are already in the window.
$(document).on("scroll", function () {
var pageTop = $(document).scrollTop()
var pageBottom = pageTop + $(window).height()
var tags = $("section")
for (var i = 0; i < tags.length; i++) {
var tag = tags[i]
if ($(tag).position().top < pageBottom) {
$(tag).addClass("visible")
} else {
$(tag).removeClass("visible")
}
}
})
section {
opacity: 0;
transform: translate(0, 20px);
transition: all 1.5s;
}
section.visible {
opacity: 1;
transform: translate(0, 0);
}
This is because everything is hidden until the first scroll event fires. To fix this you can manually trigger a scroll event when the page first loads in order to display the section elements which are already visible in the viewport.
$(document).on("scroll", function () {
// your code here...
}).trigger('scroll');
It's also worth noting that the scroll event handler fires for every pixel that you scroll by. As such performance is important there so it would be worth optimising that handler function.
var $tags = $("section");
var winHeight = $(window).height();
$(document).on("scroll", function() {
var pageTop = $(document).scrollTop();
var pageBottom = pageTop + winHeight;
$tags.each(function() {
this.classList.toggle(this.offsetTop < pageBottom)
});
}).trigger('scroll');
$(window).on('resize', function() {
winHeight = $(this).height();
});

Add and Remove class on window scroll [duplicate]

So basically I'd like to remove the class from 'header' after the user scrolls down a little and add another class to change it's look.
Trying to figure out the simplest way of doing this but I can't make it work.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll <= 500) {
$(".clearheader").removeClass("clearHeader").addClass("darkHeader");
}
}
CSS
.clearHeader{
height: 200px;
background-color: rgba(107,107,107,0.66);
position: fixed;
top:200;
width: 100%;
}
.darkHeader { height: 100px; }
.wrapper {
height:2000px;
}
HTML
<header class="clearHeader"> </header>
<div class="wrapper"> </div>
I'm sure I'm doing something very elementary wrong.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
//>=, not <=
if (scroll >= 500) {
//clearHeader, not clearheader - caps H
$(".clearHeader").addClass("darkHeader");
}
}); //missing );
Fiddle
Also, by removing the clearHeader class, you're removing the position:fixed; from the element as well as the ability of re-selecting it through the $(".clearHeader") selector. I'd suggest not removing that class and adding a new CSS class on top of it for styling purposes.
And if you want to "reset" the class addition when the users scrolls back up:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
$(".clearHeader").addClass("darkHeader");
} else {
$(".clearHeader").removeClass("darkHeader");
}
});
Fiddle
edit: Here's version caching the header selector - better performance as it won't query the DOM every time you scroll and you can safely remove/add any class to the header element without losing the reference:
$(function() {
//caches a jQuery object containing the header element
var header = $(".clearHeader");
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
header.removeClass('clearHeader').addClass("darkHeader");
} else {
header.removeClass("darkHeader").addClass('clearHeader');
}
});
});
Fiddle
Pure javascript
Here's javascript-only example of handling classes during scrolling.
const navbar = document.getElementById('navbar')
// OnScroll event handler
const onScroll = () => {
// Get scroll value
const scroll = document.documentElement.scrollTop
// If scroll value is more than 0 - add class
if (scroll > 0) {
navbar.classList.add("scrolled");
} else {
navbar.classList.remove("scrolled")
}
}
// Use the function
window.addEventListener('scroll', onScroll)
#navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 60px;
background-color: #89d0f7;
box-shadow: 0px 5px 0px rgba(0, 0, 0, 0);
transition: box-shadow 500ms;
}
#navbar.scrolled {
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.25);
}
#content {
height: 3000px;
margin-top: 60px;
}
<!-- Optional - lodash library, used for throttlin onScroll handler-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
<header id="navbar"></header>
<div id="content"></div>
Some improvements
You'd probably want to throttle handling scroll events, more so as handler logic gets more complex, in that case throttle from lodash lib comes in handy.
And if you're doing spa, keep in mind that you need to clear event listeners with removeEventListener once they're not needed (eg during onDestroy lifecycle hook of your component, like destroyed() for Vue, or maybe return function of useEffect hook for React).
Example throttling with lodash:
// Throttling onScroll handler at 100ms with lodash
const throttledOnScroll = _.throttle(onScroll, 100, {})
// Use
window.addEventListener('scroll', throttledOnScroll)
Add some transition effect to it if you like:
http://jsbin.com/boreme/17/edit?html,css,js
.clearHeader {
height:50px;
background:lightblue;
position:fixed;
top:0;
left:0;
width:100%;
-webkit-transition: background 2s; /* For Safari 3.1 to 6.0 */
transition: background 2s;
}
.clearHeader.darkHeader {
background:#000;
}
Its my code
jQuery(document).ready(function(e) {
var WindowHeight = jQuery(window).height();
var load_element = 0;
//position of element
var scroll_position = jQuery('.product-bottom').offset().top;
var screen_height = jQuery(window).height();
var activation_offset = 0;
var max_scroll_height = jQuery('body').height() + screen_height;
var scroll_activation_point = scroll_position - (screen_height * activation_offset);
jQuery(window).on('scroll', function(e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = y_scroll_pos > scroll_activation_point;
var has_reached_bottom_of_page = max_scroll_height <= y_scroll_pos && !element_in_view;
if (element_in_view || has_reached_bottom_of_page) {
jQuery('.product-bottom').addClass("change");
} else {
jQuery('.product-bottom').removeClass("change");
}
});
});
Its working Fine
Is this value intended? if (scroll <= 500) { ... This means it's happening from 0 to 500, and not 500 and greater. In the original post you said "after the user scrolls down a little"
In a similar case, I wanted to avoid always calling addClass or removeClass due to performance issues. I've split the scroll handler function into two individual functions, used according to the current state. I also added a debounce functionality according to this article: https://developers.google.com/web/fundamentals/performance/rendering/debounce-your-input-handlers
var $header = jQuery( ".clearHeader" );
var appScroll = appScrollForward;
var appScrollPosition = 0;
var scheduledAnimationFrame = false;
function appScrollReverse() {
scheduledAnimationFrame = false;
if ( appScrollPosition > 500 )
return;
$header.removeClass( "darkHeader" );
appScroll = appScrollForward;
}
function appScrollForward() {
scheduledAnimationFrame = false;
if ( appScrollPosition < 500 )
return;
$header.addClass( "darkHeader" );
appScroll = appScrollReverse;
}
function appScrollHandler() {
appScrollPosition = window.pageYOffset;
if ( scheduledAnimationFrame )
return;
scheduledAnimationFrame = true;
requestAnimationFrame( appScroll );
}
jQuery( window ).scroll( appScrollHandler );
Maybe someone finds this helpful.
For Android mobile $(window).scroll(function() and $(document).scroll(function() may or may not work. So instead use the following.
jQuery(document.body).scroll(function() {
var scroll = jQuery(document.body).scrollTop();
if (scroll >= 300) {
//alert();
header.addClass("sticky");
} else {
header.removeClass('sticky');
}
});
This code worked for me. Hope it will help you.
This is based of of #shahzad-yousuf's answer, but I only needed to compress a menu when the user scrolled down. I used the reference point of the top container rolling "off screen" to initiate the "squish"
<script type="text/javascript">
$(document).ready(function (e) {
//position of element
var scroll_position = $('div.mainContainer').offset().top;
var scroll_activation_point = scroll_position;
$(window).on('scroll', function (e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = scroll_activation_point < y_scroll_pos;
if (element_in_view) {
$('body').addClass("toolbar-compressed ");
$('div.toolbar').addClass("toolbar-compressed ");
} else {
$('body').removeClass("toolbar-compressed ");
$('div.toolbar').removeClass("toolbar-compressed ");
}
});
}); </script>

Mouseover and mouseout not firing with animating element

I need to detect if a user is hovering over an element, which is straightforward. However, these events don't seem to fire when the element is animating. If you check out my fiddle, just have the element animate past your mouse without moving your mouse, and you'll see that the events don't fire. It makes sense why this would happen, but I haven't been able to find a good way to get the behavior I want, which is to detect hovering even if the user doesn't move his/her mouse and the element animates under it.
Any thoughts?
Thanks!
Note: solutions without use of external libraries are optimal, but any help is still appreciated :)
HTML
<div id='moving'></div>
<ul id="message"></ul>
CSS
#moving {
width: 50px;
height: 50px;
background-color: red;
animation: move 7s linear;
}
#keyframes move {
from {transform: translateX(0px)}
to {transform: translateX(500px)}
}
JS
var counter = 0;
document.getElementById("moving").addEventListener("mouseover", function(){
counter++;
var node = document.createElement("LI");
var textnode = document.createTextNode("Entered " + counter);
node.appendChild(textnode);
document.getElementById("message").appendChild(node);
});
document.getElementById("moving").addEventListener("mouseout", function(){
var node = document.createElement("LI");
var textnode = document.createTextNode("Left " + counter);
node.appendChild(textnode);
document.getElementById("message").appendChild(node);
});
Here's a fiddle of it:
https://jsfiddle.net/w5j842Lx/
You can check if the mouse is in or out within an interval. Here is a working fiddle extending from your fiddle.
// This is the helper method I have written
var addMoveListener = function(element, onmouseover, onmouseout) {
var over = false;
var mouseX, mouseY;
var checkOver = function(ev) {
if (ev) {
mouseX = ev.clientX;
mouseY = ev.clientY;
}
if (mouseX == null || mouseY == null) return;
var rect = element.getBoundingClientRect();
var isInside = mouseX >= rect.left && mouseX < rect.right && mouseY >= rect.top && mouseY < rect.bottom;
if (over && !isInside && onmouseout) onmouseout();
if (!over && isInside && onmouseover) onmouseover();
over = isInside;
}
document.addEventListener("mousemove", checkOver);
var interval = setInterval(checkOver.bind(null, null), 100);
}
// Code below is for the sake of demonstration
var counter = 0;
var mouseovercallback = function() {
counter++;
console.log("Entered " + counter);
};
var mouseoutcallback = function() {
console.log("Left " + counter);
};
addMoveListener(document.getElementById("moving"), mouseovercallback, mouseoutcallback);
#moving {
width: 50px;
height: 50px;
background-color: red;
animation: move 7s linear;
}
#keyframes move {
from {
transform: translateX(0px)
}
to {
transform: translateX(500px)
}
}
<div id='moving'></div>
The code checks if the mouse is contained for every 100 miliseconds and also if the mouse is moved. If you want to handle cases where the element is not a rectangle or is rotated, skewed etc., you have to improve the code.
Take a look at this jsfiddle https://jsfiddle.net/3vpaoj59/
It includes a function like this
setInterval(checkMouse, 100);
that basically calls a function 10 times a second to check if the mouse's coordinates are within the animated shape. Your shape is a square and not a circle, so you would have to do some different math. This code is nice because it doesn't use a plugin, but it's probably CPU intensive and might have poor performance in some cases.

Animating SVG element on viewport

My question is related to my previous question Triggering css animate class on scroll
I am trying to figure out how to trigger the viewport Javascript on the inner SVG element class.
You can see here the example: http://codepen.io/anon/pen/Afqza
<div style="height: 400px;"></div>
<svg version="1.1" id="lock" xmlns="http://www.w3.org/2000/svg" width="85px" height="85px" viewBox="0 0 103 103" ><g><g><g><g><circle style="fill:#E84849;" cx="51.5" cy="51.501" r="51.125"/></g></g></g></g><g><g><g class="shackle"><path style="fill:#CFC7BE;" d="M78.345,46.518c0-14.869-11.813-28.387-26.386-28.387c-14.573,0-26.386,13.518-26.386,28.387h6.829c0-11.021,8.756-21.419,19.557-21.419s19.557,10.398,19.557,21.419H78.345z"/><path style="fill:#E8E7E7;" d="M61.385,20.101v7.816c3.039,1.927,5.583,4.717,7.362,7.975V24.879C66.562,22.886,64.076,21.26,61.385,20.101z"/></g><g><path style="fill:#F4E028;" d="M78.358,79.801c0,3.116-2.579,5.642-5.765,5.642H31.281c-3.186,0-5.764-2.525-5.764-5.642V46.419c0-3.116,52.841-3.116,52.841,0V79.801z"/></g><g><path style="fill:#DAC425;" d="M58.047,59.944c0-3.253-2.638-5.89-5.889-5.89c-3.253,0-5.889,2.637-5.889,5.89c0,2.481,1.536,4.599,3.705,5.468v5.431c0,1.151,0.935,2.084,2.085,2.084c1.153,0,2.086-0.933,2.086-2.084v-5.36C56.418,64.666,58.047,62.498,58.047,59.944z"/></g><g><path style="fill:#D0B82B;" d="M46.048,59.944c0-3.253,2.637-5.89,5.891-5.89c0,0-4.105,2.737-4.105,5.99c0,3.312,3.097,5.276,3.097,5.276v5.581c0,1.153,1.104,2.024,1.104,2.024c-1.15,0-2.085-0.933-2.085-2.084v-5.36C47.677,64.666,46.048,62.498,46.048,59.944z"/></g></g><g><polygon style="fill:#F8E349;" points="68.747,85.442 61.385,85.442 61.385,44.219 68.747,44.585 "/></g></g></svg>
.shackle {
animation-name: open;
-webkit-animation-name: open;
animation-duration: 0.5s;
-webkit-animation-duration: 0.5s;
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform-origin: bottom left;
}
#keyframes open {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(-10deg);
}
100% {
transform: rotate(0deg);
}
}
#-webkit-keyframes open {
0% {
-webkit-transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(-10deg);
}
100% {
-webkit-transform: rotate(0deg);
}
}
var onAppear = [];
document.addEventListener("DOMContentLoaded", function() {
onAppear = [].map.call(document.querySelectorAll("#lock"), function(item ) {
return item;
});
}, false);
window.addEventListener("scroll", function() {
onAppear.forEach(function(elem) {
var vwTop = window.pageYOffset;
var vwBottom = (window.pageYOffset + window.innerHeight);
var elemTop = elem.offsetTop;
var elemHeight = elem.offsetHeight;
if (vwBottom > elemTop && ((vwTop - elemHeight) < elemTop)) {
elem.classList.add("shackle");
} else {
elem.classList.remove("shackle");
}
});
}, false);
Currently the whole padlock animates instead of the shackle that I want to animate.
Must be something simple but I cannot figure it out.
The issue is that you arent applying the animation class to the shackle element, you are applying it to the lock element.
Because you are playing around with CSS3 and SVG, I can assume you dont need to accommodate for IE7 and below. Therefore, we can assume it's safe to use JS's querySelector method.
First, we'll update the style definition to indicate the class is for an animation definition (and also to separate it from the class on the shackle element that we'll use to select it).
change .shackle to .animShackle in CSS
Second, we'll need to update the scroll event listener to search within the supplied element for the .shackle classed element, and then apply the animation class to that.
update JS
JS
window.addEventListener("scroll", function() {
onAppear.forEach(function(elem) {
var vwTop = window.pageYOffset;
var vwBottom = (window.pageYOffset + window.innerHeight);
var elemTop = elem.offsetTop;
var elemHeight = elem.offsetHeight;
var shackle = elem.querySelector('.shackle');
if (vwBottom > elemTop && ((vwTop - elemHeight) < elemTop)) {
shackle.classList.add("animShackle");
} else {
shackle.classList.remove("animShackle");
}
});
}, false);
UPDATE
To make the code more extensible to the need for additional elements with their own animations we need to change some of our variable names so that they feel more universal, and update the way we are getting and setting the animation class.
Add a universal class to the animated SVG's so that we can find them in our onAppear function
add class animatedSVG
update querySelectorAll method to use new class rather than single id
Update the class name on the animated element within the SVG so that we can access it within the scroll onAppear.forEach method
update class .shackle to .animatedElement in HTML
update elem.querySelector method to use new class rather than non-generic .shackle
Use the SVG's id attribute to create a classname for animation
add a new variable called animationClass made from the SVG id with 'anim' prepended
HTML now requires the following 3 things:
id attribute on SVG
class="animatedSVG" on SVG
class="animatedElement" on element within SVG you wish to animate
Updated JS
var onAppear = [];
document.addEventListener("DOMContentLoaded", function() {
onAppear = [].map.call(document.querySelectorAll(".animatedSVG"), function(item) {
return item;
});
}, false);
window.addEventListener("scroll", function() {
onAppear.forEach(function(elem) {
var vwTop = window.pageYOffset;
var vwBottom = (window.pageYOffset + window.innerHeight);
var elemTop = elem.offsetTop;
var elemHeight = elem.offsetHeight;
var animatedElem = elem.querySelector('.animatedElement');
var animationClass = 'anim'+elem.id;
if (vwBottom > elemTop && ((vwTop - elemHeight) < elemTop)) {
animatedElem.classList.add(animationClass);
} else {
animatedElem.classList.remove(animationClass);
}
});
}, false);
DEMO

WebKit: How to determine what elements are under a touch while dragging an animating div

I'm trying to implement drag and drop for Mobile Safari.
Basically I want to drag a div via touch while it plays a looping CSS animation. I also want to determine when it's above a drop target and do something special.
I'm using elementFromPoint to determine what elements my div is being dragged over on touchmove events. However, the dragged div is always the topmost element. So before querying, I set it to display: none. However, this has the effect of resetting my animation every frame in which the touch moves.
How do I determine what I'm dragging above without resetting my CSS animation every time I query?
JSFiddle using touch events in WebKit.
HTML On every touchmove I move the drag div.
<div id='drop'>Drop here</div>
<div id='drag'>Drag me</div>
JavaScript
var drag = document.getElementById('drag');
var drop = document.getElementById('drop');
drag.addEventListener('touchmove', move, true);
function move(e) {
var touch = e.changedTouches[0];
drag.style.left = touch.pageX - 25 + 'px';
drag.style.top = touch.pageY - 25 + 'px';
drag.style.display = 'none';
if (drop === document.elementFromPoint(touch.pageX, touch.pageY)) {
drag.classList.add('in-drop-zone');
} else {
drag.classList.remove('in-drop-zone');
}
drag.style.display = 'inline';
}
CSS
div {
position: absolute;
}
div#drag {
width: 50px;
height: 50px;
background-color: blue;
-webkit-transform-origin: 50% 50%;
-webkit-animation: sway 1s infinite alternate;
}
div#drop {
width: 100px;
height: 100px;
background-color: green;
left: 200px;
top: 200px;
}
div#drag.in-drop-zone {
background-color: yellow;
}
#-webkit-keyframes sway {
from {
-webkit-transform: rotate(-30deg);
}
to {
-webkit-transform: rotate(30deg);
}
}
You could probably set pointer-events: none and then pointer-events: auto, but that seems pretty hacky. A better option is to have an intersection function:
var drag = document.getElementById('drag');
var drop = document.getElementById('drop');
var drop_bbox = drop.getBoundingClientRect();
window.addEventListener('touchmove', move, true);
window.addEventListener('mousemove', move, true);
function move(e) {
drag.style.left = e.clientX - 25 + 'px';
drag.style.top = e.clientY - 25 + 'px';
var drag_bbox = e.target.getBoundingClientRect();
drag.className = rectsIntersect(drag_bbox, drop_bbox) ? "in-drop-zone" : null;
}
function rectsIntersect(r1, r2) {
if (!r1 || !r2) return false;
return r2.left < (r1.left+r1.width) &&
(r2.left+r2.width) > r1.left &&
r2.top < (r1.top+r1.height) &&
(r2.top+r2.height) > r1.top;
};
Here is a JSFiddle http://jsfiddle.net/duopixel/SvPpw/

Categories