The wrapper overflows on x direction so it has horizontal scrolling, and the table inside .body overflows on y direction so it has vertical scrolling. The Markup looks like below.
<div class="container">
<div class="wrapper">
<div class="head">
<table>...</table>
</div>
<div class="body">
<table>...</table>
</div>
</div>
</div>
I've removed the default browser scrollbars and created custom scrollbars. The custom scrollbars are created for the container element. When wrapper scrolls horizontally then container's x-scrollbar scrolls, and when table inside body scrolls vertically then container's y-scrollbar scrolls.
The problem is with the vertical scrollbar. When I click and drag and the vertical scrollbar's thumb, I've to update the scrollTop of the table inside body. I'm founding it hard to figure out the right calculations to update the scrollTop.? Someone help me fix this.
I've set up the mousedown, mousemove and mouseup listeners for the y-scrollbar's thumb to calculate the amount of distance dragged, but how do I calculate the scrollTop of the table inside body with respect to the amount of distance dragged in the y-scrollbar?
I figured out the correct calculations.
var elem = document.querySelector('.body table'); // Content to be scrolled.
var yTrack = document.querySelector('.track.y'); // scroll-track-y
var yBar = yTrack.querySelector('.bar'); // scroll-thumb-y
On mousedown event, I'm setting the intial values to an object.
On mousemove event, I'm updating the scrollTop of the element to be scrolled.
Then on mouseup event i'm setting that object to null.
function yBar_OnMouseDown(e)
{
initCoords = {
axis: 'y',
abs: e.pageY,
bar: yBar,
p0: yBar.offsetTop
};
};
function yBar_OnMouseMove(e)
{
if (initCoords && initCoords.axis === 'y')
{
var maxElemScroll = elem.scrollHeight - elem.clientHeight;
var room = yTrack.scrollHeight - yBar.clientHeight;
var diff = e.pageY - initCoords.abs;
var abs = initCoords.p0 + diff;
var maxTrackScroll = yTrack.offsetHeight - yBar.offsetHeight;
abs = abs < 0 ? 0 : maxTrackScroll < abs ? maxTrackScroll : abs;
elem.scrollTop = maxElemScroll * abs / room;
}
};
function yBar_OnMouseUp(e)
{
initCoords = null;
};
Related
A DIV can be resize using CSS like this
resize: both;
overflow: auto;
So, when you mouse over the DIV , you will get the resize cursor on the lower right corner.
I tried to detect the cursor change to "resize" in Java Script with no luck so far. I tried to log the cursor style with this code on a "click event" when the cursor change to "resize" like this:
console.log(document.body.style.cursor);
console.log(e.target.style.cursor);
The log result id always a " " (empty) instead of a "resize" value.
Thanks in advance for your help.
Yes, I checked, string is empty for e.target.style.cursor. So I propose to solve the issue with a hack. We know, that an area where cursor changes is about 18×18 pixels, so we should detect cursor at this zone.
const textarea = document.querySelector('textarea');
const deltaX = 18;
const deltaY = 18;
const detectionOfCursorInLowerRightCorner = (e) => {
const rect = e.target.getBoundingClientRect();
const x = e.clientX; // Position X of cursor relative to the document.
const y = e.clientY; // Position Y of cursor relative to the document.
// Calculate lower and high edges of the area where cursor changes.
const lowEdgeX = rect.width - deltaX;
const highEdgeX = rect.width;
const lowEdgeY = rect.height - deltaY;
const highEdgeY = rect.height;
const elementCursorX = x - rect.x; // Position X of cursor relative to the element.
const elementCursorY = y - rect.y; // Position Y of cursor relative to the element.
console.log(' ');
console.log(elementCursorX, elementCursorY);
if (elementCursorX > lowEdgeX && elementCursorX <= highEdgeX && elementCursorY > lowEdgeY && elementCursorY <= highEdgeY) {
console.log('here'); // Cursor is in the area.
}
};
textarea.addEventListener('mousemove', detectionOfCursorInLowerRightCorner);
Do not forget do textarea.removeEventListener('mousemove', detectionOfCursorInLowerRightCorner) on element destroy. And define your own CSS class to detect when textarea has properties resize: both; and overflow: auto;.
Thanks for your answer. Indeed, I did something approximative (I skipped the Y axis logic) similar to your solution. On mousemove in the DIV, I consider that resize will occurs if my mouse X position is between the offetWidth-15px and offsetx. If not, I can drag my DIV.
I considered also the resize observer feature, but it was getting too complicated for multiple DIV elements.
The idea is to be add drag/resize events to my DIV elements for editing purpose over a canvas using absolute positioning.
Congratulation for your concise and well formatted answer and the time spent you spent on it.
How can I calculate offset top inside a scrollable div? I have two divs that I want scroll inside my content div, and I want to set 2 variables to true/false, depending on where they are positioned inside that content div.
I tried something like this but I guess it calculates the entire page offset, it doesn't really work. I bind scroll to that content div, and I want to calculate their positon:
angular.element(slideContent).bind("scroll", function () {
var contentScrollTop = angular.element(slideContent).scrollTop();
var slideOneOffset = slideOne.offset().top;
var slideTwoOffset = slideTwo.offset().top;
var firstSlideDistance = (contentScrollTop - slideOneOffset);
var secondSlideDistance = (contentScrollTop - slideTwoOffset);
});
I think this should get you most of the way there:
// Position of first element relative to container top
var scrollTop = $(".container .element").offset().top - $(".container").offset().top;
// Position of selected element relative to container top
var targetTop = $(".container > *").offset().top - $(".container").offset().top;
// The offset of a scrollable container
var scrollOffset = scrollTop - targetTop;
// Scroll untill target element is at the top of its container
$(".container").scrollTop(scrollOffset);
———————————
EDIT (May 31 2018)
I guess this is better:
var scrollOffset = $(".container .element")[0].offsetTop - $(".container")[0].offsetTop
This is the fiddle. https://jsfiddle.net/igor_g/ksjkpg0t/16/
My intention is to build a scrollbar on a strip of colors. The colors are taken from the text it is supposed to scroll. Basically, the strip is an overview of all the colors that are used in the text fonts. The text is generated dynamically and can be very large and very small. My intention is to make the scroller's size proportional and at the same time scroll properly. I have tried various combinations using jquery but I just can not seem to get the scroller to work the way I want. Sometimes, the scroller doesnt seem to point to the right text passage. How can this be fixed?
The jquery code is as follows:
/// here is our plugin declaration
(function ( $ ) {
$.fn.colorScroller = function( options ) {
var settings = $.extend({
replaceBlack: 'rgb(83, 84, 72)',
colorScrollClass: "color-map",
overlayClass: "overlay",
scrollSectionClass: "mapSection",
coloredTextClass: "passage"
}, options );
// assign this(the selector that is calling this function) to container
var container = this;
// getting the height of container
var containerHeight = container.outerHeight(true);
// creating overlay scrolling element
var $overlay = $('<div class="'+settings.overlayClass+'" ></div>')
//creating container for colored parts
var $colorScroll=$('<div class="'+settings.colorScrollClass+'" ></div>')
// appending colormap element after container in DOM
container.after($colorScroll);
// approximate total height of browser vertical scroll buttons
var browserScrollButtons = 26;
// setting height of colorscroll element to the height of container
$colorScroll.outerHeight(container.outerHeight()-browserScrollButtons );
var scrollerHandleMin = 31
// here we are calculating the total height of text elements of container
var totalHeight = 0;
container.children('.'+ settings.coloredTextClass).each(function(){
totalHeight = totalHeight + $(this).outerHeight(true);
});
// calculate height of text empty elements like br or <p></p>
var emptyHeight = 0;
container.children().not('.'+ settings.coloredTextClass).each(function(){
emptyHeight = emptyHeight + $(this).outerHeight(true);
});
// here we are calculating the ratio b/n total height of children of container and the height of color-scroll element
var ratio = totalHeight/$colorScroll.outerHeight();
// here we create a jquery object containing of small divs which will be colored, and associated with each passage div in container
var $mini = $('.'+settings.coloredTextClass, container).map(function(){
// getting the height of passage, setting the height of small div element by dividing by ratio
var heightPassage=$(this).outerHeight(true)/ratio+'px';
// getting the color of passage
var colorPassage=$(this).css('color');
// color change if color is black
if (colorPassage=='rgb(0, 0, 0)')
colorPassage = settings.replaceBlack;
var elem = $('<div class="'+settings.scrollSectionClass+'" style="height:'+heightPassage+'; background-color:'+colorPassage+'" ></div>').get(0)
// returning a jquery object element with class, height and color associated according to previous calculations
return elem
});
// get approximate height of browser scroll bar handle
var browserScrollBarHeight = (containerHeight*(containerHeight-browserScrollButtons ))/(totalHeight + emptyHeight )
browserScrollBarHeight = browserScrollBarHeight > scrollerHandleMin ? browserScrollBarHeight : scrollerHandleMin
// set overlay scroller height to browser scroll handle height
$overlay.outerHeight(browserScrollBarHeight);
var overlayHeight = $overlay.outerHeight();
// appending the minified elements into colorscroll element
$($mini).appendTo($colorScroll);
// appending overlay element into color-scroll element
$colorScroll.append($overlay);
// getting top position of container related to document top
var colorScrollTop = $colorScroll.offset().top+1+parseInt($colorScroll.css('margin-top').replace('px', ''))
// getting multiple to create algorithm for converting top positions
var k = (totalHeight + emptyHeight - containerHeight)/($colorScroll.innerHeight()-overlayHeight)
// attaching draggable to overlay scrolling element, so that it can be moved along color scroll element
$overlay.draggable({
// constrain movement to color-scroll element only
containment: "."+settings.colorScrollClass,
// what to do when overlay is dragged
drag: function() {
// getting top position of overlay related to document top
var top = $(this).offset().top;
container.animate({
// scroll container vertically to the point of overlay top (related to color-scroll element ) associated with text in container
scrollTop: (top-colorScrollTop)*k +'px'
}, 2)
},
});
/// when mouse on color-scroller unbind container scroll and enble draggable
/// when mouse leaves color-scroller bind container scroll and disable draggable
$colorScroll
.mouseenter(function() {
$overlay.draggable('enable')
container.off('scroll', scrollText)
})
.mouseleave(function() {
$overlay.draggable('disable')
container.on('scroll', scrollText)
});
// function triggered when container scrolled, basically scroll the overlay
function scrollText(){
$overlay.animate({
top: container.scrollTop()/k + 'px'
}, 1)
}
container.on('scroll', scrollText)
// when the color-scroll element is clicked
$colorScroll.on('click',function(e){
// calculate the position of click related to color-scroll itself
var topPosition = e.pageY-$(this).offset().top;
// and move overlay top to that position
$overlay.animate({
top: topPosition+'px'
}, 200)
// and scroll container to text associated with overlay top
container.animate({
scrollTop: (topPosition+overlayHeight)*ratio-containerHeight+'px'
}, 10)
})
return container;
};
}( jQuery ));
/// end plugin
$(document).ready(function() {
var passes = [0, 1, 2, 3, 4, 5, 6]
for (var i in passes) {
col = document.getElementById('pass' + i).style.color;
var element = document.createElement("section");
element.style.background = col;
document.getElementById("left").appendChild(element);
}
canvas = document.createElement("canvas");
canvas.id = 'canvas';
canvas.style = 'height:700px;width:50px;';
/// here we init plugin function
$('#document_content').colorScroller({replaceBlack: 'rgb(0,0,0)'});
/// the other option with defaults are :
// colorScrollClass: "color-map", css class of colored scrollbar
// overlayClass: "overlay", css class of the custom scrollbar
// scrollSectionClass: "mapSection", css class of the small colored sections in colored scroll bar
// coloredTextClass: "passage" css class of the original text part
///
});
The code:
http://codepen.io/anon/pen/EjrpMM
So, i'm working on an interesting problem. I am working with a 2000px HTML document, that has a modal placed ontop of it.
The width of the div lightbox is 80%, and it's sitting positioned fixed.
The goal is, when scrolling down the page, to control the width of the div based on the scroll position. At the bottom of the page, it's only a third in size.
I've had trouble figuring out the proper equation or formula for this, and was seeking help.
Currently, I've been trying to look at the window.pageYOffset, to add 2.5% to the div while increasing, and minus 2.5% when scrolling back up, to bring it back to it's 80% width.
However, something isn't working right. I was seeing if the community had any ideas to help solve the problem.
I'm not using any frameworks for this.
Here's my javascript:
var lightBox = document.getElementById('lightBox'),
count = 80,
num = window.pageYOffset;
document.addEventListener('scroll', function(e) {
var offset = window.pageYOffset;
num >= offset ? count += 2.5 : count -= 2.5;
num = offset;
lightBox.style.width = count + '%';
});
View the code here, in this codepen
http://codepen.io/anon/pen/EjrpMM
Thank you!
You just have to change
+= 2.5 and -=2.5 to += 0.7 and -= 0.7
When I checked your code I did this and it worked.
Scroll event fired once on scroll independently on how much you've scrolled. E.g. if you've scrolled 1px scrollbar, or scrolled 100px using mousewheel scroll event will be fired once.
So if you need stable results you will need to calculate your div width depending on scroll position (window.pageYOffset).
Check this codepen fork. I've assumed that in the end of page div width should be 50%.
Core part is:
var lightBox = document.getElementById('lightBox');
var upperBound = 80;
var lowerBound = 50;
var tickValue = (upperBound - lowerBound) / window.innerHeight;
document.addEventListener('scroll', function(e) {
var offset = window.pageYOffset;
var count = upperBound - offset * tickValue;
lightBox.style.width = count + '%';
});
Important note: for crossbrowser way to get innerHeight you can check this answer
This is a simple equation. Let f : scroll |-> f(scroll) be the function that gives you the width of your div. You want f(0) = 0.8, f(2000)= 1/3.
Let's say you want the progression to be linear so f(scroll) = a*scroll + b, you can easily deduce that b = 0.8 and a = (1/3 - 0.8)/2000 = -0.000233. Now for any scroll value, you can find the width of your div.
Now you can change the values when you want, f(scroll) = (minWidth-maxWidth)/pageLength * scroll + maxWidth.
Greeting's
I'am still relatively new to JavaScript and Jquery and I know there has got to be a better method than the one I'am using.
I'am working on a serialScroll implementation. There is a master scrollTo that controls the left/right movement of a number of slides. Each slide contains it's own implementation of a vertical serialScroll. I have the resize on the scrollTo working great and the resize on the vertical works but I can't figure an elegant method for ensuring that on resize the current position remains centered, My current method works but is very inefficient.
$(function(){
var $up = $('#sec1_nav a.up').hide();//up button -- each slide needs it's own unique nav buttons
var $down = $('#sec1_nav a.down');//down button -- each slide needs it's own unique nav buttons
$('#screen1').serialScroll({
target:'#section1',
items:'.item',
prev:'#sec1_nav a.up',
next:'#sec1_nav a.down',
axis:'y',
duration:1000,
force:true,
cylce: false,
onBefore:function( e, elem, $pane, $items, pos ){
$up.add($down).show();
if( pos == 0 )$up.hide();
else if( pos == $items.length -1 )
$down.hide();
// Here's where it get ugly, I'am adding a unique class for each slide to the item's
$('.item').removeClass('pos1'); //Each slides need's it's own class i.e. slide1 = pos1, slide2 = pos2 etc.
$(elem).addClass('pos1');
}
});
$(window).bind("resize", function(){
resize1(); // Same goes for the resize function, each slide need's it's own function.
});
});
function resize1() {
height = $(window).height();
width = $(window).width();
mask_height = height * $('.item').length; // sets the new mask height
// Resize Height of the area
$('.sections').css({height: height, width : width});
$('.item').css({height: height, width : width});
$('#mask').css({height : mask_height, width : width});
$('.sections').scrollTo('.pos1', 0 ); // This issue is where it all fall apart, instead of using serialScroll, i'am stuck using scrollTo to maintain the current slide position.
}