Always scroll a div element and not page itself - javascript

I have a page layout with an inner <div id="content"> element which contains the important stuff on the page. The important part about the design is:
#content {
height: 300px;
width: 500px;
overflow: scroll;
}
Now when the containing text is larger than 300px, I need to be able to scroll it. Is it possible to scroll the <div>, even when the mouse is not hovering the element (arrow keys should also work)?
Note that I don’t want to disable the ‘global’ scrolling: There should be two scrollbars on the page, the global scrollbar and the scrollbar for the <div>.
The only thing that changes is that the inner <div> should always scroll unless it can’t be moved anymore (in which case the page should start scrolling).
Is this possible to achieve somehow?
Edit
I think the problem was a bit confusing, so I’ll append a sequence of how I would like it to work. (Khez already supplied a proof-of-concept.)
The first image is how the page looks when opened.
Now, the mouse sits in the indicated position and scrolls and what should happen is that
First the inner div scrolls its content (Fig. 2)
The inner div has finished scrolling (Fig. 3)
The body element scrolls so that the div itself gets moved. (Fig. 4)
Hope it is a bit clearer now.
(Image thanks to gomockingbird.com)

I don't think that is possible to achieve without scripting it, which could be messy, considering the numerous events which scroll an element (click, scrollwheel, down arrow, space bar).

An option could be using the jQuery scroll plugin. I know it has the availability to create scrollbars on an div. The only thing you need to add yourself is the logic to catch the events when keyboard buttons are pressed. Just check out the keycodes for the arrow keys and make the div scroll down.
The plugin can be found here.
You can use it like this;
<script type="text/javascript">
// append scrollbar to all DOM nodes with class css-scrollbar
$(function(){
$('.css-scrollbar').scrollbar();
})
</script>

here is a solution that might work: (fiddle: http://jsfiddle.net/maniator/9sb2a/)
var last_scroll = -1;
$(window).scroll(function(e){
if($('#content').scrollTop());
var scroll = $('#view').data('scroll');
if(scroll == undefined){
$('#content').data('scroll', 5);
scroll = $('#content').data('scroll');
}
else {
$('#content').data('scroll', scroll + 5);
scroll = $('#view').data('scroll');
}
/*
console.log({
'window scroll':$('window').scrollTop(),
'scroll var': scroll,
'view scroll':$('#view').scrollTop(),
'view height':$('#view').height(),
'ls': last_scroll
});
//*/
if(last_scroll != $('#content').scrollTop()){ //check for new scroll
last_scroll = $('#content').scrollTop()
$('#content').scrollTop($('#content').scrollTop() + scroll);
$(this).scrollTop(0);
//console.log(e, 'scrolling');
}
})
It is a bit buggy but it is a start :-)

The only way I believe you can achieve this is through the use of frames.
Frames - W3Schools Reference

If you just want to have a fixed positioned "div" and scroll only it, maybe you could use a trick like:
http://jsfiddle.net/3cpvT/
Scrolling with mouse wheel and all kinds of keys works as expected. Only thing is that the scrollbar is on the document body only.

I found a solution... Not perfect... http://jsfiddle.net/fGjUD/6/.
CSS:
body.noscroll {
position: fixed;
overflow-y: scroll;
width: 100%;
}
JS (jQuery):
if ($("body").height() > $(window).height()) {
var top;
$('#scrolldiv').mouseenter(function() {
top = $(window).scrollTop();
$('body').addClass('noscroll').css({top: -top + 'px'});
}).mouseleave(function() {
$('body').removeClass('noscroll');
$(window).scrollTop(top);
});
}
The text wrapping problem can be solved putting the whole content in fixed-width div. There is another bug for IE browser. If the page has center-aligned backgrond, it will move left-right on mouseenter on #scrolldiv

Related

JavaScript function to manipulate CSS custom properties not working on $(window).resize()

I have a fixed header with :target in-page anchors, and need to adjust the property values dynamically via JavaScript or JQuery so as to maintain the relevant :target's position directly under the header when the window is resized, while adapting to the changes in both the previous section's .container height and the .header_container height that occur with resizing.
The simplest solution seems to be a ::before pseudo-element for the :target pseudo-class, and to then utilize CSS custom properties to dynamically modify the style properties.
I have no trouble correctly positioning the :target with the below function when the page is loaded (or reloaded), or correctly position the first :target on $(window).resize(), however it's failing to do the same for the remaining targets on $(window).resize().
Fiddles
Simplified Code: https://jsfiddle.net/chayanyc/g6p3549s/
Responsive Design (Simplified): https://jsfiddle.net/chayanyc/wuk92dns/
Code Snippets
CSS:
.header_container {height: 98px; margin: 0; padding: 0; position: fixed; top: 0; display: block; z-index: 100;}
.main {margin-top: 98px; width: 100%;}
:target::before {height: var(--target_position1); margin-top: var(--target_position2); content: ""; display: block; visibility: hidden;}
JavaScript:
var headerHeight;
function setTarget() {
headerHeight = document.getElementById('header').offsetHeight;
headerHeight1 = headerHeight + "px";
document.documentElement.style.setProperty('--target_position1', headerHeight1);
document.documentElement.style.setProperty('--target_position2', '-' + headerHeight1);
}
$(window).resize(function () {
setTarget();
});
$(document).ready(function () {
setTarget();
});
There is no complete solution to this Problem,
because you want the target element stay on place on document resize, but if the user do a scroll on his page, it is not possible to know where staying on the same first word of the first line on display.
So here, i just replace on the same target on top when user resize his document, even if he had done a scroll just before.
no need of this CSS part (remove it)
:target::before {margin: 0; content: ""; dis.....
and change your jQuery to:
$(document).ready(function () {
// global info for menu -> target elememt
var InfoTarget = { ID: null, tempo:300 }
$('a').click(function(evt){
InfoTarget.ID = $(this).attr('href') // possible target elm
// check if InfoTarget.ID exist on page
let nbElements = 0
try { nbElements = $(InfoTarget.ID).length }
catch(err) { nbElements = 0 }
if ( nbElements != 1 ) {
InfoTarget.ID = null // not target element found
}
else {
evt.preventDefault() // disable auto scroll to target element
$('html').animate({
scrollTop: ($(InfoTarget.ID).offset().top - $('#header').outerHeight(true))
}, InfoTarget.tempo );
}
});
$(window).resize(function (){
if (InfoTarget.ID) { // if InfoTarget.ID exist <=> InfoTarget.ID != null
$('html').animate({
scrollTop: ($(InfoTarget.ID).offset().top - $('#header').outerHeight(true))
}, InfoTarget.tempo);
}
});
});
My code speaks for itself, but here is a complete explanation:
the principle is simple: the function target css activates on a click on a link <a href="#..."> to trigger a scroll of the page towards the element having for id = to that contained in the initial href.
therefore this code intercepts any click on a link on the page and must first determine whether it is a link to an anchor or not.
To determine if this is a link to an anchor on the page, it simply tests whether an element of the page has this value as this ID, (// check if InfoTarget.ID exists on page).
As this kind of test can also generate an error, this test is placed in a try / catch.
If the result is indeed an anchor, then the action of the click is canceled, (with evt.preventDefault()) which prevents the browser from triggering its automatic scroll to the link;
the reference link is kept in an object variable (global)
var InfoTarget = {ID: null, tempo: 300}
seen on: InfoTarget.ID = $(this).attr('href') // possible target elm
the rest is simple, you have to scroll down to the anchor.
Depending on the width of the page and the previous elements, browsers continuously recalculate the position of each tag present on a page and jQuery can be retrieved this offset position by $(element).offset().Top
as there is a menu bar on your page that masks the top of the page, you must deduct its height from the position in scroll (= $ ('# header'). outerHeight (true))
a scroll = 0 will force a move to the top of the page
a scroll = $(element).offset().top places the element at the top of the page
to which we must deduct the height of the #header
the complete formula is
scrollTop: ( $(InfoTarget.ID).offset().top - $('#header').outerHeight(true) )
this command is placed in a jQuery.animate, for a visually smoother move, and uses the InfoTarget.tempo value as the duration for this animation.
During a resize of the page, and to the extent that a link having a target has been previously clicked (therefore always active) then the same type of scroll is triggered.
The different jQuery methods used are all explained in the jQuery doc (for example: https://api.jquery.com/outerHeight/ )
New Solution -- Lundi 14 oct 2019 / 01:00 (in the night of sunday / monday)
this script must be placed after all the html elements of the body
// scroll to target upon window.location.hash
$(window).on('hashchange', function() {
$('.TargetMark').removeClass('TargetMark')
$(window.location.hash).addClass('TargetMark')
setTimeout( scrollTop2, 220 ) // scroll to target after browser auto scrolling conflit
})
function scrollTop2() {
if ($('.TargetMark').length===1) { // if target exist
$('html').animate({
scrollTop: ($('.TargetMark').offset().top - $('#header').outerHeight(true))
}, 100);
}
}
In this version the target element is added a class (TargetMark) allowing to find it when window resize
ending part
$(document).ready(function () {
//...
// ---------------------------> no call to scrollTop();
//...
});
$(window).resize(function () {
//...
scrollTop2();
//...
});
about toggleMenu conflict:
function toggleMenu() {
$('.navbar-toggle').on('click', function () {
if ($("#js-menu").is(".expand")) {
$("#js-menu").toggleClass("expand");
$("#submenu").removeClass("active_sub").addClass("inactive_sub");
} else {
$("#js-menu").toggleClass("expand");
$("#submenu").removeClass("inactive_sub").addClass("active_sub");
}
resetTarget();
setTimeout( scrollTop2, 220 ) // scroll to target after browser auto scrolling conflit
});
}
I spent a lot of my time on your question, I studied differents approaches and the different automatisms put at work by the navigators themselves and which is necessary to fight to get the result you'r looking for. I came to the conclusion that the problem first came from the architecture of your page.
The fact that the menu ("#header") covers the page ("#main") is a major flaw that prevents to have an effective JS code for your question.
The call on the hash of an anchor triggers one or more scrolls of the page, the resize of the page also entails a scroll calculation because by changing size on the screen, it also changes the size of the page. page (reducing the size of the screen by half makes the page size double), it is the same by changing the size of the font, it also changes the size in page.
Whenever the page size changes, the browser must recalculate a lot of things and some of these mechanisms can trigger one or more scrolls.
What you are asking here is to recalculate a page positioning according to an element of which we can not be certain that it is completely established because this process is executed in parallel with other processes of the browser which can change useful values.
Plus the fact that some of the browser processes also work to scroll the page and that it can be the last done!
So the fact that there is an overlap between the menu and the page add more complexity and makes the possibility of a solution impossible.
Change your layout and 3/4 of your problem will be fixed.
Resize is firing, offset height is not changing. Setting the same value over and over again, yields no change. You might check this:
see the value change
I used the logo for output:
$('.logo').text(headerHeight + ' -' + i++);
You want to scroll down to the one div selected by target without having it to be overlapped by your nav?
.. then extend the areas. see here
add positive margin-top and negative padding-top.
.... to compensate for any nav size changes, use media queries to change your css vars.

How to link page scroll to a div element scroll? Or how to scroll div element using page scroll?

So I have an issue with scrolling the page and some div with content inside it.
There is a .container at the bottom of the page and footer goes after it.
When an user gets to the bottom of the page there should be possibility to continue scrolling the page but exactly the .scrollable container should be scrolled.
By default we can scroll .scrollable div's content if mouse cursor is over it. But I need to somehow link common page scroll to this .scrollable div's scroll.
How does this problem can be solved?
Here the JSFiddle link to make the issue more clear
$(window).on('mousewheel', function(e) {
//scrolling bottom
if(e.originalEvent.wheelDelta /120 <= 0) {
//checks if we reached bottom
if($(this).scrollTop() + $(this).height() == $(document).height()) {
$('.scrollable').scrollTop($('.scrollable').scrollTop() + 10);
}
}
});
EDIT: After lots of digging I've managed to build a script for exactly what you need
NOTE: The event is currently bound on mousewheel but there are more types of scrolling such as: dragging, clicking, arrow keys and I'm not aware of function to cover them all and do the thing you want in the same time.
I forked your Fiddle

How to stick a div at the bottom of the page while scrolling after checkbox is checked

Usually I don't ask questions...I'm looking for a solution until I give up,
and this is the case here.
There are many similar questions to my but after a thorough search I found nothing.
So the question is:
After selecting a checkbox the div at the bottom of the page
shuold be sticky untill the user scrolling down to the original place where it was.
I have a great example from kickstarter web site :
If only I could know how they do it :)
If I was not clear enough I'd love to explain myself better.
Thanks in advance
After clicking on checkbox,
You can add these CSS lines to div
position:fixed;
bottom:0;
you want to add position: fixed and attach it to the bottom of the container when checked
html
<div class="wrapper">
<input type="checkbox" id="check"/>
<div id="foot"></div>
</div>
js
var check = document.getElementById('check');
var foot = document.getElementById('foot');
check.addEventListener('change', function () {
if (check.checked) {
foot.style.position = 'fixed';
foot.style.bottom = 0;
}
});
fiddle - http://jsfiddle.net/qak2ept6/
EDIT - http://jsfiddle.net/qak2ept6/1/ restore when unchecked
EDIT EDIT - http://jsfiddle.net/qak2ept6/3/ attach on scroll
when you check the check box. create div with position fixed and store the offset of the bottom edge of the window that would be normally your window height. Assign scroll event and keep checking if the scroll value is equal to the offset you have stored and when it reached just remove the fixed position from the div.
My guess (and if I was doing it) It'll be done by monitoring scroll position and applying a css style or not accordingly.
Something like
Inject it in invisible state in to the document
Note it's position (y coord)
Apply class to make it stick to the bottom of the window and show
On scroll, as soon as you get near the expected yCoord, remove the class and let it assume it's rightful place in the document
On further scroll (when you scroll away), re-apply class until you scroll back
HTH
If i have understood your question, I guess what you want is here
function sticky_relocate() {
var window_top = $(window).scrollTop();
var div_top = $('#sticky-anchor').offset().top
if (window_top > div_top) {
$('#sticky').addClass('stick');
} else {
$('#sticky').removeClass('stick');
}
}
$(function () {
$(window).scroll(sticky_relocate);
sticky_relocate();
});
If not, please explain us with more code and what exactly you need

How to keep jQuery function from moving elements all the way to the left?

I was able to implement the solution posted here ("position: fixed and absolute at the same time. HOW?") to get a div element to move with the rest of the page horizontally, but stay fixed vertically. However, this solution causes the selected element to move ALL the way to the left of the page (with what appears to be a 20px margin). I'm still new to javascript and jQuery, but as I understand it, the following:
$(window).scroll(function(){
var $this = $(this);
$('#homeheader').css('left', 20 - $this.scrollLeft());});
takes the selected element and, upon scrolling by the user, affects the CSS of the element so that its position from the left becomes some function of the current scrollbar position adjusted by the 20px margin. If this is correct? And if so, can anyone think of a way that I could change it so that instead of moving the selected element all the way to the left side of the window, we only move it as far left as my default CSS position for the body elements of the HTML document (shown below)?
body {font-size:100%;
width: 800px;
margin-top: 0px;
margin-bottom: 20px;
margin-left: auto;
margin-right: auto;
padding-left: 20px;
padding-right: 20px;}
EDIT: Here is a jsfiddle (code here) that I made to illustrate the issue. My page is designed so that when it is displayed in full-screen or near full-screen mode, the #homeheader element appears centered horizontally due to its width and the left and right margins being set to auto. As the page gets smaller and smaller the margins do as well, until they disappear altogether and are replaced by the padding-left and padding-right settings of 20px. So at this point (when the window is small enough that the margins disappear altogether), which is what the jsfiddle shows, the code appears to work as intended, but when the window is full-sized the JS overrides the CSS and pushes the div element all the way to the left (getting rid of the margin) upon any scrolling action.
There are two events you need to handle to get this to work correctly. First is the scroll event which you are pretty close on. The only thing you might want to do is to use offset to get the current left position value based on the document.
The other event which is not yet handled is the resize event. If you don't handle this then once a left position is defined your element (header) will be there always regardless of whether or not the user resizes the window.
Basically something like this should work:
var headeroffset;
var header = $('#homeheader');
// handle scroll
$(window).scroll(function() {
// auto when not defined
if (headeroffset === undefined) {
// capture offset for current screen size
headeroffset = header.offset();
}
// calculate offset
var leftOffset = headeroffset.left - $(this).scrollLeft();
// set left offset
header.css('left', leftOffset);
});
// handle resize
$(window).resize(function() {
// remove left setting
// (this stops the element from being stuck after a resize event
if (header.css('left') !== 'auto') {
header.css('left', '');
headeroffset = undefined;
}
});
JSFiddle example: http://jsfiddle.net/infiniteloops/ELCq7/6/
http://jsfiddle.net/infiniteloops/ELCq7/6/show
This type of effect can be done purely in css however, i would suggest taking a look at the full page app series Steve Sanderson did recently.
http://blog.stevensanderson.com/2011/10/05/full-height-app-layouts-a-css-trick-to-make-it-easier/
As an example you could do something like this:
http://jsfiddle.net/infiniteloops/ELCq7/18/
Try this
$('#homeheader').css('left', parseInt($('body').css('margin-left')) - $this.scrollLeft());});
What I did here is just replace 20 with body's left-margin value.

Chrome Extension Scrollbar Placement

After dabbling in Chrome Extensions I've noticed that when the data inside the Page Action gets to a certain point the scroll bars automatically attach themselves to the popup, this I expect. However, instead of pushing the content to the left of the scroll bar it overlays the content causing a horizontal scrollbar to become active. I ended up just adding a check on my data and applying a css class to push the content to the left more to run parallel to the scroll bar and beside it not under it. What is the correct way to handle this besides my hackish solution?
I was wondering this myself too. Currently I just don't put anything important closer than 20px to the right side of a popup and disable horizontal scrollbars:
body {overflow-x:hidden;overflow-y:auto;}
So when a vertical scrollbar appears the content at least doesn't jump.
Perhaps you need to specify a width on the scrollbar.
::-webkit-scrollbar {
width: 42px; //Do not know actual width, but I assume you do
}
I haven't found a way to do this that isn't a hack, but here's the simplest hack I could think of:
<script type="text/javascript">
function tweakWidthForScrollbar() {
var db = document.body;
var scrollBarWidth = db.scrollHeight > db.clientHeight ?
db.clientWidth - db.offsetWidth : 0;
db.style.paddingRight = scrollBarWidth + "px";
}
</script>
...
<body onresize="tweakWidthForScrollbar()">
The idea is to detect whether the vertical scrollbar is in use, and if it is, calculate its width and allocate just enough extra padding for it.

Categories