I'm having an issue transforming my horizontal scroll logic into positional one. To summarize my problem; I have a div which has overflowing elements, which need to be scrollable, but not with scrollbars/overflow, but position:left. I had OK solutions, but it all went downhill after resetting on click.
The following snippet is where I was left stranded, when you click and drag the child, it should act the same as if you were horizontally scrolling.
Also, please note, I must use mousedown/mousemove/mouseup events so that I can port the logic over to touch
Thanks in advance!
var $scrollWithin = $('#child');
var clicked = false, clickX;
$(document).on({
'mousemove': function(e) {
clicked && updateScrollPos(e);
},
'mousedown': function(e) {
clicked = true;
clickX = e.pageX;
},
'mouseup': function() {
clicked = false;
}
});
var lastLeft = 0;
var updateScrollPos = function(e) {
var left = lastLeft + (clickX - e.pageX);
$scrollWithin.css('left', left + 'px');
lastLeft = left;
}
#child {
height:200px;
width:4000px;
background: linear-gradient(to right, rgba(169,3,41,1) 0%,rgba(0,80,255,1) 100%);
position:relative;
}
#parent {
width:300px;
overflow:hidden;
border:solid 4px #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="parent">
<div id="child"></div>
</div>
Related
I have a sticky up arrow image for my webpage as follows:
HTML:
<img src = "images/sticky-btn-light.png" id = "myBtn" alt = "sticky-up-button">
And CSS:
#myBtn {
visibility: hidden;
position: fixed;
bottom: 50px;
right: 30px;
z-index: 99;
cursor: pointer;
padding: 15px;
border-radius: 10px;
width: 5%;
opacity: 0.5 ;
}
The button disappears when the user scrolls down and appears when the user scrolls up based on this JS code.
window.onscroll = function(e) {
console.log(this.oldScroll > this.scrollY);
if((this.scrollY == 0) || (this.oldScroll < this.scrollY) ){
document.getElementById('myBtn').style.visibility = 'hidden';
}
else if(this.oldScroll > this.scrollY){
document.getElementById('myBtn').style.visibility = 'visible';
}
this.oldScroll = this.scrollY;
}
Now, I want to change the color of the button based on its changing position on the screen. As I scroll the page, the sticky button will be in different sections as below.
If the sticky button is in Section A, it should be red. And if it is in Section B, it should be blue. Please note that it's the page that is moving, not the button. The button is in a fixed position.
For this, I need the id of the section in which the sticky button is overlapping at any given moment. Is there any way to get that information through JavaScript?
PS: I have adjusted the details to make things more clear. It's the page that is moving. So, if I use Element.getBoundingClientRect() for #myBtn, will I not get the same top/y values for that element wherever I scroll on the page?
You can use element.getBoundingClientRect() to get the x,y of the top left corner and the x,y of the bottom right corner of a element.
var arrow = document.getElementById('myBtn');
var arrowRect = arrow.getBoundingClientRect();
console.log(arrowRect.top, arrowRect.right, arrowRect.bottom, arrowRect.left);
var section = document.getElementById('section1');
var sectionRect = section.getBoundingClientRect();
console.log(sectionRect.top, sectionRect.right, sectionRect.bottom, sectionRect.left);
Then you can check collisions with the arrow and the section. In your case the x-axis doesn't matter:
// This checks if the arrow is touching the section
( arrowRect.bottom > sectionRect.top && arrowRect.top < sectionRect.bottom )
// This checks if the arrow isn't touching the section, then inverts it (faster)
!( arrowRect.bottom < sectionRect.top || arrowRect.top > sectionRect.bottom )
Here, I did an example for you to test and implement.
this will also help understand getBoundingClientRect even with fixed position
var arrow= document.getElementById('myBtn');
window.onscroll = function(e) {
var arrowBound = arrow.getBoundingClientRect();
var divs = document.querySelectorAll(".container > div");
divs.forEach(function(item){
var divBound = item.getBoundingClientRect();
var color= item.getAttribute("arrowColor");
if ( arrowBound.bottom > divBound.top && arrowBound.top < divBound.bottom )
{
arrow.style.borderTopColor = color
}
})
}
#myBtn {
position: fixed;
top: 10px;
left:270px;
z-index: 99;
cursor: pointer;
width: 0;
height: 0;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-top: 20px solid #f00;
}
.container{
width:300px;
height:800px;
z-index: 81;
}
.container > div {
width:300px;
height:100px;
border:1px solid black;
}
<div class="container">
<a id = "myBtn" href = "#navbar-scroll"></a>
<div arrowColor="red"> box red </div>
<div arrowColor="blue"> box blue </div>
<div arrowColor="green"> box green </div>
</div>
You can get coords of a html element with getBoundingClientRect() method, call this function and you'll get the Y coords of element (pixels), then you can use an if conditional
function getCoords(elem) {
let box = elem.getBoundingClientRect();
let body = document.body;
let docEl = document.documentElement;
let scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
let clientTop = docEl.clientTop || body.clientTop || 0;
return Math.round(box.top + scrollTop - clientTop);
}
if(getCoords(elem) > 100){
elem.className = 'your_class_name'
}
.your_class_name{
color: red;
}
How to move or drag the span into the Div element. My element structure is the Div -> Span. Here I need to drag the Span inside the div element without drag beyond that div. I have tried this by calculating pixels but didn't give a solution. I don't need a native onDrag method.
I need to calculate pixels and drag the Span inside the Div. Here is my code.
var handleClick = false;
window.dragging = function(event) {
if (handleClick) {
var bar = document.getElementsByClassName('bar')[0],
handle = document.getElementsByClassName('handle')[0];
var left = bar.offsetWidth - handle.offsetWidth;
tops = (bar.offsetWidth - handle.offsetWidth);
pixel = left < ((pixel - 0) / 1.233445) ? left : ((pixel - 0) / 1.233445);
handle.style.left = pixel + "px";
}
}
document.addEventListener('mouseup', function() {
handleClick = false;
});
window.handlersDown = function() {
handleClick = true;
}
.bar {
width: 100px;
height: 30px;
border: 1px solid;
position: relative;
}
.handle {
width: 20px;
height: 20px;
left: 2px;
top: 5px;
background-color: rgba(255, 0, 0, 0.5);
position: absolute;
}
<div class="bar">
<span class="handle" onmousedown="handlersDown()" onmousemove="dragging(event)"></span>
</div>
I have modified your code a bit and changed the selectors from class to ID. I also would advice you to use external libraries to make it more easy for you. Besides that I also removed the event listeners inside your HTML and translate them to Javascript. Is this what you want?
window.onload = addListeners();
function addListeners(){
document.getElementById('handle').addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);
}
function mouseUp()
{
window.removeEventListener('mousemove', spanMove, true);
}
function mouseDown(e){
window.addEventListener('mousemove', spanMove, true);
}
function spanMove(e){
var bar = document.getElementById('bar')
var span = document.getElementById('handle');
// variables
var bar_width = bar.offsetWidth;
var handle_width = span.offsetWidth;
// stop scroll left if the minimum and maximum is reached
if(e.clientX < bar_width - handle_width - 1 && e.clientX > 1){
span.style.left = e.clientX + 'px';
}
}
#bar {
width: 100px;
height: 30px;
border: 1px solid;
position: relative;
}
#handle {
width: 20px;
height: 20px;
left: 2px;
top: 5px;
background-color: rgba(255, 0, 0, 0.5);
position: absolute;
}
<div id="bar">
<span id="handle"></span>
</div>
In 2020, following solution works perfectly on last version of Chrome, Opera, Firefox and Edge Chromium.
window.onload = addListeners();
function addListeners()
{
var div = document.getElementById('div');
var span = document.getElementById('span');
span.addEventListener('mousedown', onMouseDown, false);
window.addEventListener('mouseup', onMouseUp, false);
//compute space between left border of <div> and left border of <span>
// this value is also used to compute space at right
iMinLeft = span.offsetLeft;
// compute max left value allowed so that span remains in <div>
iMaxLeft = div.clientWidth - span.offsetWidth - iMinLeft;
}
function onMouseDown(e)
{
if (e.which === 1) // left button is pressed
{
e.preventDefault();
window.addEventListener('mousemove', onMouseMove, true);
// save mouse X position to compute deplacement
posMouseX = e.clientX;
span.style.background = "yellow";
}
}
function onMouseMove(e)
{
e.preventDefault();
//compute mouse deplacement
deltaX = posMouseX - e.clientX;
//compute new left position of <span> element
iNewLeft = span.offsetLeft - deltaX;
if (iNewLeft < iMinLeft)
{
iNewLeft = iMinLeft;
}
else
{
if (iNewLeft > iMaxLeft)
{
iNewLeft = iMaxLeft;
}
}
span.style.left = iNewLeft + 'px';
// save mouse X position to compute NEXT deplacement
posMouseX = e.clientX;
}
function onMouseUp(e)
{
if (e.which === 1) // left button is pressed
{
e.preventDefault();
span.style.background = "white";
window.removeEventListener('mousemove', onMouseMove, true);
}
}
#div
{
width: 200px;
height: 50px;
border: 1px solid;
position: relative;
left: 50px;
}
#span
{
cursor: pointer;
font-size: 30px;
width: auto;
height: 40px;
left: 2px;
top: 5px;
position: absolute;
}
<div id="div">
<span id="span">😃</span>
</div>
JavaScript line e.preventDefault(); is necessary to avoid <span> to become 'blue' when dragging.
CSS code cursor: pointer; is only to see that unicode is clickable.
Javascript line if (e.which === 1) has been added to prevent emoticon to move when RIGHT mouse button is clicked.
The rectangle around emoticon when <span> is dragged move without being shifted (see previous solution) and space remaining in left or in right are equal.
Thanks to w3schools-exemple
Hi i have a div which is draggable .My requirement is
1. I want the start value and end value of the div after dragging in a text box
2.Now its is only drag in right side only i want it drag also from left side of the div
i tried some steps but it is not perfect because the value is not displaying and the dragging from left is not working middle table is my parent div
$(function () {
var container = $('.middletablediv'),
base = null,
handle = $('.handle'),
isResizing = false,
screenarea = screen.width;
handle.on('mousedown', function (e) {
base = $(this).closest(".scalebar");
isResizing = true;
lastDownX = e.clientX;
offset = $(this).offset();
xPos = offset.left;
});
$(document).on('mousemove', function (e) {
// we don't want to do anything if we aren't resizing.
if (!isResizing)
return;
p = parseInt(e.clientX - base.offset().left),
// l = parseInt(p * (3 / 11));
base.css('width', p);
k = parseInt(xPos - base.offset().left);
$("#startvalue").value(k)
$("#stopvalue").value(p)
}).on('mouseup', function (e) {
// stop resizing
isResizing = false;
});
});
.handle{
position: absolute;
top:1px;
right: 0;
width: 10px;
height: 5px;
cursor: w-resize;
}
.middletablediv{
float:left;
width:35%;
}
.scalebar{
margin-top: 13px;
height: 7px;
position: relative;
width:20px;
background-color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="middletablediv">
<div id="newvalue1" class="scalebar">
<div class="handle" style="left:0"></div> <div class="handle"></div>
</div>
</div>
<input id="startvalue" type="text">startvalue</input>
<input id="stopvalue" type="text" />stopvalue</input>
how i solve this issue
You should use val() instead of value(). Also, the way dragging works the end value can be smaller than the start value, so I added a couple of things to handle that problem in a simple way (just switch values). Finally, to drag from the left, you should handle the left dragging differently, so I gave the left handle a unique id and also padded the whole parent div a bit to make it more apparent.
$(function () {
var container = $('.middletablediv'),
base = null,
handle = $('.handle'),
isResizing = false,
isLeftDrag = false;
screenarea = screen.width;
handle.on('mousedown', function (e) {
base = $(this).closest(".scalebar");
isResizing = true;
if($(this).attr('id')=='lefthandle')isLeftDrag=true;
else isLeftDrag=false;
lastDownX = e.clientX;
offset = $(this).offset();
xPos = offset.left;
});
$(document).on('mousemove', function (e) {
// we don't want to do anything if we aren't resizing.
if (!isResizing)
return;
if(isLeftDrag){
p = parseInt(base.offset().left - e.clientX);
k = parseInt(base.offset().left - xPos);
base.css('margin-left',-p);
base.css('width',p);
}
else{
p = parseInt(e.clientX - base.offset().left),
// l = parseInt(p * (3 / 11));
base.css('width', p);
k = parseInt(xPos - base.offset().left);
}
//if(k>p){var temp = k; k = p; p = temp;}
$("#startvalue").val(k)
$("#stopvalue").val(p)
}).on('mouseup', function (e) {
// stop resizing
isResizing = false;
});
});
.handle{
position: absolute;
top:1px;
right: 0;
width: 10px;
height: 5px;
cursor: w-resize;
}
.middletablediv{
float:left;
width:35%;
}
.scalebar{
margin-top: 13px;
height: 7px;
position: relative;
width:20px;
background-color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="middletablediv" style="padding-left:100px; overflow:visible;">
<div id="newvalue1" class="scalebar">
<div class="handle"id="lefthandle" style="left:0"></div> <div class="handle"></div>
</div>
</div><br><br>
<input id="startvalue" type="text">startvalue</input>
<input id="stopvalue" type="text" />stopvalue</input>
I'm trying to detect when my div gets opened is the top of it outside of the view-port & if it is add a class to adjust the css.
So basically in this example on hover half of the div is missing so that should then add the class that would turn the div green. Because the code is meant to detect that the div is outside the viewport.
But I just cant get it to sync. I'm obviously doing something wrong here.
UPDATE
I have just noticed that it technically is working what is happening if both the top and bottom of the div goes outside of both the bottom and top of the view port then it triggers. I need it to only trigger when it goes out of the top.
JSfiddle
$(document).on("mouseenter", ":has('.infotip')", function() {
$(this).children(".infotip").addClass("active");
});
$(document).on("mouseleave", ":has('.infotip')", function() {
$(this).children(".infotip").removeClass("active");
});
// Infotip on screen
$(document).on("mouseenter", ":has('.infotip.onscreen')", function() {
var $target = $(this).children(".infotip");
if ($target.length) {
var $bounce = $target.offset().top + $target.height();
if ($bounce > $(window).height()) {
$target.addClass("test");
} else {
$target.addClass("top");
}
}
});
.infotip {
display: none;
height:500px;
width:100px;
position:absolute;
left:50px;
top:-250px;
}
.infotip.active {
display: block;
}
/* goes red when past top of viewport (which it will not do in this example) */
.infotip.top {
background-color: rgba(249, 14, 18, 1.00)
}
/* goes green if visible (which it should do when hovering) */
.infotip.test {
background-color: rgba(35, 223, 51, 1.00)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div class="team-card align">
hover me
<div class="infotip onscreen">
Iam infotip
</div>
</div>
Here was the issue, on-hover it detect div height and if it overflows then changes the div to green.
$(document).on("mouseenter", function() {
var $target = $(".team-card").children(".infotip");
if ($target.length) {
var $bounce = $target.offset().top + $target.height();
if ($bounce > $(window).height()) {
$target.addClass("test");
} else {
$target.addClass("top");
}
}
});
So I figured this out myself. Finally had a brain storm after realising what the height was down as mentioned on my update.
What I should have been doing is calculating the distance from the top and then detecting if that distance is less than one e.g. 0, -5, -250 etc. Then kick my statement in.
Just wasn't thinking about this one in the right manner for a bit.
JSFiddle
$(document).on("mouseenter", ":has('.infotip')", function() {
$(this).children(".infotip").addClass("active");
});
$(document).on("mouseleave", ":has('.infotip')", function() {
$(this).children(".infotip").removeClass("active");
});
// Infotip on screen
$(document).on("mouseenter", ":has('.infotip.onscreen')", function() {
var $target = $(this).children(".infotip");
if ($target.length) {
var scrollTop = $(window).scrollTop(),
elementOffset = $target.offset().top,
bounce = (elementOffset - scrollTop);
if (bounce < 1 ) {
$target.addClass("test");
} else {
$target.addClass("top");
}
}
});
.infotip {
display: none;
height:500px;
width:100px;
position:absolute;
left:50px;
top:-250px;
}
.infotip.active {
display: block;
}
/* goes red when past top of viewport (which it will not do in this example) */
.infotip.top {
background-color: rgba(249, 14, 18, 1.00)
}
/* goes green if visible (which it should do when hovering) */
.infotip.test {
background-color: rgba(35, 223, 51, 1.00)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div class="team-card align">
hover me
<div class="infotip onscreen">
Iam infotip
</div>
</div>
I'm trying to make a range slider but it's working in single direction(to right) only and dragging out of parent container(#volume). How can I fix this?
I've attached a demo fiddle link.
Markup
<div id="volume">
<div class="progress">
<div class="volumeslider"></div>
</div>
</div>
CSS
#volume{
width:300px;
background: #ddd;
cursor:pointer;
}
.progress{
height:10px;
background:#999;
position:relative;
width:0;
}
.volumeslider {
background: #808080;
position: absolute;
width: 20px;
height: 20px;
border-radius: 15px;
right: -10px;
top: -5px;
}
JS
$('.volumeslider').bind('mousedown', function(e){
$('.volumeslider').bind('mousemove', function(e){
$('.progress').width(e.pageX - $('.progress').offset().left + 'px');
$(this).css('left', e.pageX - ($(this).width()/2) );
});
$('.volumeslider').bind('mouseup',function(){
$('.volumeslider').unbind('mousemove');
});
});
JS Fiddle:
http://jsfiddle.net/2mYm7/
You have not taken into consideration the padding you have given to the body element.
I have made some changes to the code.
$('.volumeslider').bind('mousedown', function (e) {
console.log('binded');
$('.volumeslider').bind('mouseup', function (e) {
console.log('unbinded');
$('.volumeslider').unbind('mousemove');
});
$('.volumeslider').bind('mousemove', function (e) {
console.log('mousemove');
$('.progress').width(e.pageX - $('.progress').offset().left + 'px');
$(this).css('left', e.pageX - 25- $(this).width());
});
});
Check this jsFiddle http://jsfiddle.net/2mYm7/1/
Here's an example of how to make it work always and everywhere. Right now it will stay dragging forever if you leave the element.
It includes border checks and makes sure the body is large enough so it stops dragging wherever on the page.
http://jsfiddle.net/2mYm7/5/
var dragging = null;
function startDrag(){
console.log('started dragging', this);
var $this = $(this);
dragging = $this;
$(document.body).bind('mouseup', stopDrag);
$(document.body).bind('mousemove', drag);
}
function stopDrag(){
console.log('stopped dragging', dragging[0]);
$(document.body).unbind('mouseup', stopDrag);
$(document.body).unbind('mousemove', drag);
dragging = null;
}
function drag(e){
var slider = dragging;
var progress = slider.parent();
var container = progress.parent();
var maxOffset = container.width();
progress.width(Math.min(e.pageX - progress.offset().left, maxOffset) + 'px');
}
$('.volumeslider').bind('mousedown', startDrag);