How do I create an invisible scrollable area on an HTML page? - javascript

I want to trigger an event whenever the user scrolls up or down inside an invisible div (a 'scroller'). Imagine the below setup :
CSS
#scroller {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 50px;
}
#scroller div {
position: absolute;
top: 0px;
left: 0px;
height: 50000px;
width: 100%;
}
span {
position: absolute;
top: 20px;
left: 100px;
}
HTML
<div id="scroller"><div></div></div>
<span></span>
Javascript
var timeout;
$("#scroller").scroll(function ()
{
clearTimeout(timeout);
$('span').text('scrolling');
timeout = setTimeout(function ()
{
$('span').text('');
}, 1000);
});
Whenever the user scrolls inside the above div, the word "scrolling" should appear on the screen. You can play around with this fiddle : http://jsfiddle.net/f1hxndt4/4/
There are two problems with the above :
Scrolling inside the 'scroller' obviously needs to be infinite (up and down) - Currently it only allows a 50000px scroll.
The "scroller" needs to be invisible. Currently the scrollbars are visible.
Any suggestions would be much appreciated, thank you!

Here is the solution in case anyone is interested : http://jsfiddle.net/f1hxndt4/14/
CSS
#scroller{
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 50px;
overflow: hidden;
}
#scroller .parent{
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100px;
overflow-x:hidden;
}
#scroller .child {
position: absolute;
top: 0px;
left: 0px;
height: 50000px;
width: 100%;
}
span {
position: absolute;
top: 20px;
left: 100px;
}
HTML
<div id="scroller">
<div class="parent">
<div class="child"></div>
</div>
</div>
<span></span>
Javascript
var timeout;
$("#scroller .parent").scroll(function ()
{
clearTimeout(timeout);
$('span').text('scrolling');
timeout = setTimeout(function ()
{
$('span').text('');
}, 1000);
});
Explanation :
You need to create a scrollable <div> : $('#scroller .parent') and then place that inside a narrower <div> : $('#scroller'). Set the overflow of the latter to 'hidden'.
That way the scrollbar on the right side of $('#scroller .parent') will not be visible anymore.

If you bind to the 'scroll' event, then you will need to make the area scrollable (which as you say, defeats the point of the what you're trying to acheive!). Instead, you need to listen for the events that would otherwise usually cause scrolling, such as listening for mousehweel events. You may also wish to listen for swipe events etc.
You can calculate scroll distance by using the wheelData property of the event to detemrine the scroll delta. (In Firefox and opera you will need to use the detail property instead.)
var onMouseWheelEvent = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll"
: "mousewheel";
var timeout;
$("#scroller").on(onMouseWheelEvent, function (e)
{
clearTimeout(timeout);
$('span').text('scrolling');
var scrollEvent = e.originalEvent;
var delta = scrollEvent.detail? scrollEvent.detail*(-120) : scrollEvent.wheelDelta
console.log(e.originalEvent.wheelDelta);
timeout = setTimeout(function ()
{
$('span').text('');
}, 1000);
});

Demo: http://jsfiddle.net/techsin/o2n2q5p4/
Improved: http://jsfiddle.net/techsin/o2n2q5p4/1/
This is similar to link you posted however it dosen't rely on scrolled up amount but creates its own amount relying on mousewheel data. I tried to solve your original problem instead.
if anything is unclear just ask: (no jquery used just for challenge)
var a=0, topSpeed = 20, deg=0;
window.addEventListener('mousewheel', function(e){
if (a<topSpeed) {
a = a + ((e.wheelDelta/1000) * topSpeed);
}
});
var img = document.getElementById('gear');
function animate() {
a = +(a*.95).toFixed(2);
if (Math.abs(a)<1) a=0;
deg = (deg+a) % 360;
img.style.transform = 'rotate('+deg+'deg)';
requestAnimationFrame(animate);
}
animate();

Related

Child element is causing issues with parent onmousemove event listener

I have created a very simple example of my problem.
Fiddle Link
In the fiddle, I have created a div named parent containing 2 imgs (i take divs in the example for simplicity but in my project, these are images) and a controller div. I place the images on the top of each other by positioning 2nd image as absolute.
I want to clip the 2nd image using clip-path property whenever, I click and then drag the controller" over the parent div.
But the controller div is causing issue with parent mousemove event whenever cursor goes on controller div, mouseout event is fired on parent div causing glitch in animation.
Adding pointer-events: none property to controller div fix the glitch but it also takes away every kind of mouse interaction from the element and I want click and drag effect.
I want to create similar effect used in this website.
The problem seems to be that the positioning of the controller sometimes (not always) 'interferes' with the reading of offsetX on the parent. And the offset goes down (to 0 or up to about 10 in the given fiddle). Hence you get the flickering as the controller moves back and then up along again.
I cannot at the moment totally explain this, particularly since the controller is an absolutely positioned element.
However, one solution is to move the controller out of the parent.
UPDATE It is though possible to leave the controller in the parent if one ignores any mousemove within the controller (so we don't get readings of 0 to 10 for the offset when the mousemove is within the controller - ignore them and we'll get the event bubbling through to the parent and can then take a reading of offset).
_
<head>
<style>
* {
margin: 0;
padding: 0;
}
#parent {
width: 100%;
position: relative;
}
#img1, #img2 {
display: block;
width: 100%;
height: 200px;
pointer-events: none;
}
#img1 {
background: red;
}
#img2 {
background: green;
position: absolute;
top: 0;
left: 0;
}
#controller {
position: absolute;
top: 0;
left: 10px;
width: 10px;
height: 100%;
height: 200px;
background: black;
z-index: 1;
cursor: ew-resize;
/* pointer-events: none; */
}
</style>
</head>
<body>
<div id="parent">
<div id="img1"></div>
<div id="img2"></div>
<div id="controller"></div>
</div>
<h4>
Click and Drag the controller to clip the front image
</h4>
<!-- img1, img2 are images in my case so i named them as imgs -->
<script>
const parent = document.getElementById('parent'),
img2 = document.getElementById('img2'),
controller = document.getElementById('controller');
let pressed = false;
console.log(pressed)
parent.addEventListener('mousemove', (e) => {
if(!pressed) return;
if (e.target != parent) return;
img2.style.clipPath = `inset(0px 0px 0px ${e.offsetX}px)`;
controller.style.left = `${e.offsetX}px`;
});
// for testing purpose
/* parent.addEventListener('mouseout', (e) => {
console.log('mouse out is called');
}); */
controller.addEventListener('mousedown', (e) => {
pressed = true;
});
controller.addEventListener('mouseup', (e) => {
pressed = false;
});
</script>
</body>
const parent = document.getElementById('parent'),
img2 = document.getElementById('img2'),
controller = document.getElementById('controller');
let pressed = false;
parent.addEventListener('mousemove', (e) => {
if (pressed) {
img2.style.clipPath = `inset(0px 0px 0px ${e.clientX - parent.offsetLeft}px)`;
controller.style.left = `${e.clientX - parent.offsetLeft}px`;
}
});
controller.addEventListener('mousedown', (e) => {
pressed = true;
});
controller.addEventListener('mouseup', (e) => {
pressed = false;
});
#parent {
width: 100%;
position: relative;
}
#img1,
#img2 {
display: block;
width: 100%;
height: 200px;
pointer-events: none;
}
#img1 {
background: red;
}
#img2 {
background: green;
position: absolute;
top: 0;
left: 0;
}
#controller {
position: absolute;
top: 0;
left: 10px;
width: 10px;
height: 100%;
background: black;
z-index: 1;
cursor: ew-resize;
/* pointer-events: none; */
}
<div id="parent">
<div id="img1"></div>
<div id="img2"></div>
<div id="controller"></div>
</div>
<h4>
Click and Drag the controller to clip the front image
</h4>
The problem is, you used offsetX which defines the distance between the top left edge of your controller element. This means the distance is about 5px, your controller jumps to 5px from left, the distance is bigger now, the controller jumps back and so on.
The offsetX read-only property of the MouseEvent interface provides
the offset in the X coordinate of the mouse pointer between that event
and the padding edge of the target node.
So therefore you can use the difference between the mouse x-position and the x-position of parent for positioning your controller:
Instead use clientX which gets the mouse position relative to the window.
img2.style.clipPath = `inset(0px 0px 0px ${e.clientX - parent.offsetLeft}px)`;
controller.style.left = `${e.clientX - parent.offsetLeft}px`;
Top expression has following meaning:
<mouse x-position> - <distance between left screen edge and parent>

How to not make div stack?

I am using this code to make the images and text move around the screen. My problem is that sometimes the images and text land on eachother and stack up, how do i make it so its not possible to land on eachother?
$(document).ready(function() {
animateDiv('.a');
animateDiv('.b');
animateDiv('.c');
animateDiv('.d');
animateDiv('.e');
animateDiv('.f');
animateDiv('.g');
animateDiv('.h');
animateDiv('.i');
});
function makeNewPosition() {
var h = $(window).height() - 60;
var w = $(window).width() - 60;
var nh = Math.floor(Math.random() * h);
var nw = Math.floor(Math.random() * w);
return [nh, nw];
}
function animateDiv(myclass) {
var newq = makeNewPosition();
$(myclass).animate({
top: newq[0],
left: newq[1]
}, 2000, function() {
animateDiv(myclass);
});
};
div.a {
width: 50px;
height: 50px;
position: fixed;
color: Navy;
}
div.b {
width: 50px;
height: 50px;
position: fixed;
color: red;
}
div.c {
width: 50px;
height: 50px;
position: fixed;
color: Fuchsia;
}
div.d {
width: 50px;
height: 50px;
position: fixed;
color: SpringGreen;
}
div.e {
position: fixed;
}
div.f {
position: fixed;
}
div.g {
position: fixed;
}
div.h {
position: fixed;
}
div.i {
position: fixed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='a'>MOCKO</div>
<div class='b'>MOCKO</div>
<div class='c'>MOCKO</div>
<div class='d'>MOCKO</div>
<div class='e'><img src="image/image1.png"></div>
<div class='f'><img src="image/image2.png"></div>
<div class='g'><img src="image/image3.png"></div>
<div class='h'><img src="image/image4.png"></div>
<div class='i'><img src="image/image5.png"></div>
Thanks beforehand, if you can help me, please do! My name is Lukas and I am not so good at coding.
So you are effectively randomly placing fixed elements on the screen, that's why they overlap occasionally.
There are several different ways to prevent that, but it just depends on what your end goal is.
If you really want the random placement approach without overlaps, you will have to run a check in makeNewPosition against all existing locations to determine if their coordinates are overlapping, and if so then regenerate coordinates. You would need to determine the edges of both the new placement and the old placement then compare them.
Personally, I think that will be a fragile and overly complicated way to do things. I would encourage you to look at other possible solutions other than the random generation, perhaps a different layout.
Also #hungerstar has a great comment. This is essentially collision detection. It can get heavily involved to do this well and answer your particular problems.
Here is a helpful SO link that explains a basic approach: jQuery/JavaScript collision detection
And here is a working example on jsfiddle: https://jsfiddle.net/ryanoc/TG2M7/
I can dive into a specific use case if you'd like. Not sure what all to post since I'm not sure what your end goal is.

SlideUp Jquery for modal popup

I have created an email subscription popup modal. Right now I have the modal set to fade in but instead of that I want the modal to slide up from bottom of the page to the top. I tried slideUp() instead of fadeIn() but it does not work. I feel like I am missing something. Do I need to create another function for the slideUp()? It is a popup modal so it is ready when the window scrolls instead of on a click event. Any help is appreciated. Thank you. My code is below.
$(document).scroll(function() {
if (!$("#mc_embed_signup").data("userClosed")) {
$(".popup-close").click(function(e) {
closeSPopup(e);
});
var a = $(this).scrollTop();
if (a > 400) {
$("#mc_embed_signup").slideUp(600);
}
}
});
function closeSPopup(e) {
e.preventDefault();
$("#mc_embed_signup").data("userClosed", true);
$("#mc_embed_signup").hide();
}
jQuery .slideUp() hides the matched elements with a sliding motion. If you want to slide your popup up, you could use .animate()
$("#popup").show().animate({top: (window.innerHeight / 2 - 50) + "px"}, 1000);
#popup {
display: none;
width: 200px;
height: 100px;
position: fixed;
top: 100%;
left: calc(50% - 100px);
background-color:cyan;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="popup"><div>
.slideDown() should be used, but!.. instead of using top: NN in CSS use the bottom: NN. This way the element "anchors" to it's bottom property, and the .slideDown() will perform from bottom to top!
var $popup = $("#popup");
$popup.slideDown();
#popup {
position: absolute;
transform: translate(-50%, 0);
bottom: 24px; /* don't use top. Use bottom */
left:50%;
width: 300px;
height: 160px;
background:red;
display: none;
}
<div id="popup">Popup ou yeah</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

jQuery X-Ray Effect to Reveal Data Points

I have a client who wants to have an X-Ray effect that reveals clickable data points. A slider would be used to move a viewing window over an image, that would reveal an x-ray, or secondary image as the slider is moved. I've adapted Eli Kirk's X-Ray effect (http://elikirk.com/2013/12/02/draggable-x-ray-effect-using-css-javascript/) to get what I have so far: http://jsfiddle.net/xfxLx/3/. The jQuery UI portion of it is easy enough:
var artWidth = 300;
$(document).ready(function() {
$('.xraySlider').slider({
slide: function(e, ui) {
var newLeft = (ui.value / 100) * (artWidth - 100);
$('.xrayWindow').css({'background-position': (newLeft * -1) + 'px 0px', "left": newLeft + "px" });
}
});
});
The problem I'm having is making clickable data points that would be revealed by the window as it is slid across the main image. The data points would be fairly simple shapes (like, say, a black circle), that the user could click on once revealed by the x-ray window, to reveal a popup with more info. I've beat my head against the wall trying to come up with a workable solution (if this wasn't bad enough, it all has to work in IE7, so I've ruled out canvas as well).
If this effect won't work under the confines listed (which I've told them might be the case, since I have yet to come up with a viable solution), that's fine, but I just want to make sure I'm not missing anything.
I have changed yoput HTML, setting an inner element to the xray, that will hold the points
<div class="artifact-hold">
<div class="artifact">
<div class="xrayUpper"></div>
<div class="xrayWindow">
<div class="innerXray">
<div class="point" id="brain"></div>
<div class="point" id="heart"></div>
</div>
</div>
<br />
<div class="xraySlider"></div>
</div>
</div>
Then, the JavaScript changes slightly
var artWidth = 300;
$(document).ready(function() {
$('.xraySlider').slider({
slide: function(e, ui) {
var newLeft = (ui.value / 100) * (artWidth - 100);
$('.xrayWindow').css({"left": newLeft + "px" });
$('.innerXray').css({"left": -newLeft + "px" });
}
});
});
And CSS is changed to make the xray clip the contents (with overflow hidden), and the inner has the background image instead of the xraywindow. also, some styling to the points.
.innerXray {
width: 300px;
height: 490px;
position: absolute;
background: url(http://s21.postimg.org/tpg6me1vb/bones.jpg) no-repeat;
background-position: 0px 0px;
}
.point {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: red;
}
#heart {
left: 150px;
top: 130px;
}
#brain {
left: 150px;
top: 30px;
}
.xrayWindow {
width: 100px;
height: 490px;
border: 2px solid rgba(255,255,255,0.5);
position: absolute;
top: 0px;
left: 0px;
overflow: hidden;
}
result

Circular rotating/magnifying menu with jquery

I'm trying to make a menu that contains 5 items/icons with the selected one being in the center. Clicking to the left or right of this centered icon, rotates the menu left or right, wrapping round the edges and moving whichever item was closest to the edge back in through the opposite one. Clicking on the centered item takes you to its linked URL.
The menu should also magnify in a way similar to the OS X dock except the magnification levels are set based on position not mouseover.
I've made a diagram which is easier to understand than my ramblings.
(source: yfrog.com)
I've managed to cobble together a simple jQuery version, where the items swap positions as needed, but can't figure out how to animate this movement, especially the wrap around the edges part, and change size based on position.
I'm guessing my code is probably not the best either :)
The HTML is as follows:
<div id="nav">
<div id="leftnav"></div>
<div id="rightnav"></div>
<div id="navblock1" class="navblock">
one
</div>
<div id="navblock2" class="navblock">
two
</div>
<div id="navblock3" class="navblock">
three
</div>
<div id="navblock4" class="navblock">
four
</div>
<div id="navblock5" class="navblock">
five
</div>
And the JS:
function rotateNav(direction) {
var change = (direction=='left')?(-1):(+1);
$('div.navblock').each(function() {
oldPos = parseInt($(this).attr('id').substr(9));
newPos = oldPos+change;
if (newPos == 0)
newPos = 5;
else if (newPos == 6)
newPos = 1;
$(this).attr('id','navblock'+newPos);
});
}
$(document).ready(function(){
$("#leftnav").click(function() {
rotateNav('right');
});
$("#rightnav").click(function() {
rotateNav('left');
});
});
All the .navblock elements are absolutely positionned. The #leftnav and #rightnav elements also and they have a higher z-index so float above the items/icons.
I've looked at various jQuery plugins but none seem close to what I need.
Instead of changing id attributes (which you really shouldn't do in the first place) you can change CSS classes and use jQuery UI's switchClass() method to animate the rotation.
You would also have to do a bit of clone()ing to make it look like the edge navblocks have rotated around to the other side of the widget and some queue()/dequeue()ing to handle multiple clicks.
Working Demo:
http://jsbin.com/ovemu (editable via http://jsbin.com/ovemu/edit)
Full Source:
JavaScript
function rotateNav(direction) {
if (direction === 'left') {
var change = 1;
$('.navblock5').clone()
.removeClass('navblock5')
.addClass('navblock0')
.appendTo('#nav');
}
else {
var change = -1;
$('.navblock1').clone()
.removeClass('navblock1')
.addClass('navblock6')
.appendTo('#nav');
}
$('div.navblock').each(function() {
var oldClassName = this.className.split(' ')[1],
oldPos = parseInt(oldClassName.substr(8)),
newPos = oldPos + change;
$(this).switchClass(
oldClassName,
'navblock'+newPos,
'fast',
function () {
var animated = $('.navblock:animated').length;
if (newPos === 6 || newPos === 0) {
$(this).remove();
}
if (animated === 1) {
$('#nav').dequeue();
}
}
);
});
}
$(document).ready(function(){
$("#leftnav").click(function() {
$('#nav').queue(function(){rotateNav('right');});
});
$("#rightnav").click(function() {
$('#nav').queue(function(){rotateNav('left');});
});
});
CSS
#nav {
width: 580px; height: 120px;
position: relative; left: 150px;
overflow: hidden;
}
.navblock {
height: 100px; width: 100px;
position: absolute; top: 10px; z-index: 50;
background-color: grey;
}
.navblock0 { left: -110px; }
.navblock1 { left: 10px; }
.navblock2 { left: 120px; }
.navblock3 { left: 230px; width: 120px; height: 120px; top: 0;}
.navblock4 { left: 360px; }
.navblock5 { left: 470px; }
.navblock6 { left: 590px; }
#leftnav, #rightnav {
position: absolute; z-index: 100; height: 120px; width: 228px;
}
#leftnav { left: 0; }
#rightnav { right: 0; }
/*Uncomment the following to help debug or see the inner workings */
/*
#nav { border: 1px solid green; overflow: visible; }
#leftnav, #rightnav { border: 1px solid blue; }
*/
HTML
<div id="nav">
<div id="leftnav"></div>
<div id="rightnav"></div>
<div class="navblock navblock1">one</div>
<div class="navblock navblock2">two</div>
<div class="navblock navblock3">three</div>
<div class="navblock navblock4">four</div>
<div class="navblock navblock5">five</div>
Instead of doing this yourself and wasting time on getting this to work properly I suggest you use existing solutions ones. Here a few pointers (I guess many more can be found by using google
jQuery: Mac-like Dock
Mac-like icon dock (v2)
MAC CSS Dock Menu
jQuery mimicking the OS X dock
Simple OSX-like dock with jQuery
iconDock jQuery Plugin
You seem to be on the right track. One issue is that this line
oldPos = parseInt($(this).attr('id').substr(9));
Should use 8 in the substr:
oldPos = parseInt($(this).attr('id').substr(8));

Categories

Resources