Suppose I have one div in my page. how to detect the user click on div content or outside of div content through JavaScript or JQuery. please help with small code snippet.
thanks.
Edit: As commented in one of the answers below, I only want to attach an event handler to my body, and also want to know which element was clicked upon.
Here's a one liner that doesn't require jquery using Node.contains:
// Get arbitrary element with id "my-element"
var myElementToCheckIfClicksAreInsideOf = document.querySelector('#my-element');
// Listen for click events on body
document.body.addEventListener('click', function (event) {
if (myElementToCheckIfClicksAreInsideOf.contains(event.target)) {
console.log('clicked inside');
} else {
console.log('clicked outside');
}
});
If you're wondering about the edge case of checking if the click is on the element itself, Node.contains returns true for the element itself (e.g. element.contains(element) === true) so this snippet should always work.
Browser support seems to cover pretty much everything according to that MDN page as well.
Using jQuery:
$(function() {
$("body").click(function(e) {
if (e.target.id == "myDiv" || $(e.target).parents("#myDiv").length) {
alert("Inside div");
} else {
alert("Outside div");
}
});
})
#myDiv {
background: #ff0000;
width: 25vw;
height: 25vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myDiv"></div>
Using jQuery, and assuming that you have <div id="foo">:
jQuery(function($){
$('#foo').click(function(e){
console.log( 'clicked on div' );
e.stopPropagation(); // Prevent bubbling
});
$('body').click(function(e){
console.log( 'clicked outside of div' );
});
});
Edit: For a single handler:
jQuery(function($){
$('body').click(function(e){
var clickedOn = $(e.target);
if (clickedOn.parents().andSelf().is('#foo')){
console.log( "Clicked on", clickedOn[0], "inside the div" );
}else{
console.log( "Clicked outside the div" );
});
});
Rather than using the jQuery .parents function (as suggested in the accepted answer), it's better to use .closest for this purpose. As explained in the jQuery api docs, .closest checks the element passed and all its parents, whereas .parents just checks the parents.
Consequently, this works:
$(function() {
$("body").click(function(e) {
if ($(e.target).closest("#myDiv").length) {
alert("Clicked inside #myDiv");
} else {
alert("Clicked outside #myDiv");
}
});
})
What about this?
<style type="text/css">
div {border: 1px solid red; color: black; background-color: #9999DD;
width: 20em; height: 40em;}
</style>
<script type="text/javascript">
function sayLoc(e) {
e = e || window.event;
var tgt = e.target || e.srcElement;
// Get top lef co-ords of div
var divX = findPosX(tgt);
var divY = findPosY(tgt);
// Workout if page has been scrolled
var pXo = getPXoffset();
var pYo = getPYoffset();
// Subtract div co-ords from event co-ords
var clickX = e.clientX - divX + pXo;
var clickY = e.clientY - divY + pYo;
alert('Co-ords within div (x, y): ' + clickX + ', ' + clickY);
}
function findPosX(obj) {
var curleft = 0;
if (obj.offsetParent) {
while (obj.offsetParent) {
curleft += obj.offsetLeft;
obj = obj.offsetParent;
}
} else if (obj.x) {
curleft += obj.x;
}
return curleft;
}
function findPosY(obj) {
var curtop = 0;
if (obj.offsetParent) {
while (obj.offsetParent) {
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
} else if (obj.y) {
curtop += obj.y;
}
return curtop;
}
function getPXoffset() {
if (self.pageXOffset) {
// all except Explorer
return self.pageXOffset;
} else if (
document.documentElement &&
document.documentElement.scrollTop
) {
// Explorer 6 Strict
return document.documentElement.scrollLeft;
} else if (document.body) {
// all other Explorers
return document.body.scrollLeft;
}
}
function getPYoffset() {
if (self.pageYOffset) {
// all except Explorer
return self.pageYOffset;
} else if (
document.documentElement &&
document.documentElement.scrollTop
) {
// Explorer 6 Strict
return document.documentElement.scrollTop;
} else if (document.body) {
// all other Explorers
return document.body.scrollTop;
}
}
</script>
<div onclick="sayLoc(event);"></div>
(from http://bytes.com/topic/javascript/answers/151689-detect-click-inside-div-mozilla, using the Google.)
This question can be answered with X and Y coordinates and without JQuery:
var isPointerEventInsideElement = function (event, element) {
var pos = {
x: event.targetTouches ? event.targetTouches[0].pageX : event.pageX,
y: event.targetTouches ? event.targetTouches[0].pageY : event.pageY
};
var rect = element.getBoundingClientRect();
return pos.x < rect.right && pos.x > rect.left && pos.y < rect.bottom && pos.y > rect.top;
};
document.querySelector('#my-element').addEventListener('click', function (event) {
console.log(isPointerEventInsideElement(event, document.querySelector('#my-any-child-element')))
});
For bootstrap 4 this works for me.
$(document).on('click', function(e) {
$('[data-toggle="popover"],[data-original-title]').each(function() {
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
$(this).popover('hide')
}
});
});
working demo on jsfiddle link:
https://jsfiddle.net/LabibMuhammadJamal/jys10nez/9/
In vanilla javaScript - in ES6
(() => {
document.querySelector('.parent').addEventListener('click', event => {
alert(event.target.classList.contains('child') ? 'Child element.' : 'Parent element.');
});
})();
.parent {
display: inline-block;
padding: 45px;
background: lightgreen;
}
.child {
width: 120px;
height:60px;
background: teal;
}
<div class="parent">
<div class="child"></div>
</div>
If you want to add a click listener in chrome console, use this
document.querySelectorAll("label")[6].parentElement.onclick = () => {console.log('label clicked');}
function closePopover(sel) {
$('body').on('click', function(e) {
if (!$(event.target).closest(sel+', .popover-body').length) {
$(sel).popover('hide');
}
});
}
closePopover('#elem1');
closePopover('#elem2');
Instead of using the body you could create a curtain with z-index of 100 (to pick a number) and give the inside element a higher z-index while all other elements have a lower z-index than the curtain.
See working example here: http://jsfiddle.net/Flandre/6JvFk/
jQuery:
$('#curtain').on("click", function(e) {
$(this).hide();
alert("clicked ouside of elements that stand out");
});
CSS:
.aboveCurtain
{
z-index: 200; /* has to have a higher index than the curtain */
position: relative;
background-color: pink;
}
#curtain
{
position: fixed;
top: 0px;
left: 0px;
height: 100%;
background-color: black;
width: 100%;
z-index:100;
opacity:0.5 /* change opacity to 0 to make it a true glass effect */
}
I've been trying to show an element on scroll when it's in viewport and when no, hide it again. But no matter what I try, I can't make it work.
This is what I have so far, but the function is running just once, when the page is loaded and not when it's scrolled, so it doesn't update the value.
$(window).scroll(function() {
var top_of_element = $("#cont_quote blockquote").offset().top;
var bottom_of_element = $("#cont_quote blockquote").offset().top + $("#cont_quote blockquote").outerHeight();
var bottom_of_screen = $(window).scrollTop() + window.innerHeight;
var top_of_screen = $(window).scrollTop();
if((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)){
$('#cont_quote blockquote').animate({'opacity':'1'},1000);
}
else {
$('#cont_quote blockquote').animate({'opacity':'0'},1000);
}
});
<section id="cont_quote">
<article class="cont_q">
<blockquote>Lorem ipsum</blockquote>
</article>
</section>
In pure javascript, you could do something like this, which uses a lot less resources than a full on jQuery approach:
function inViewport( element ){
// Get the elements position relative to the viewport
var bb = element.getBoundingClientRect();
// Check if the element is outside the viewport
// Then invert the returned value because you want to know the opposite
return !(bb.top > innerHeight || bb.bottom < 0);
}
var myElement = document.querySelector( 'div' );
// Listen for the scroll event
document.addEventListener( 'scroll', event => {
// Check the viewport status
if( inViewport( myElement ) ){
myElement.style.background = 'red';
} else {
myElement.style.background = '';
}
})
body {
height: 400vh;
}
div {
width: 50vw;
height: 50vh;
position: absolute;
top: 125vh;
left: 25vw;
transition: background 4s;
border: 1px solid red;
}
<p>Scroll Down</p>
<div></div>
Here is a snippet with the opacity change:
function inViewport( element ){
// Get the elements position relative to the viewport
var bb = element.getBoundingClientRect();
// Check if the element is outside the viewport
// Then invert the returned value because you want to know the opposite
return !(bb.top > innerHeight || bb.bottom < 0);
}
var myElement = document.querySelector( 'div' );
// Listen for the scroll event
document.addEventListener( 'scroll', event => {
// Check the viewport status
if( inViewport( myElement ) ){
myElement.style.opacity = 1;
} else {
myElement.style.opacity = '';
}
})
body {
height: 400vh;
}
div {
width: 50vw;
height: 50vh;
position: absolute;
top: 125vh;
left: 25vw;
transition: opacity 1s;
opacity: .2;
background: blue;
}
<p>Scroll Down</p>
<div></div>
And here is a snippet showing you how to define where in the viewport it triggers, I just changed the innerHeight and 0 values to an object where you define the amount of pixels from the top it should be and the amount of pixels from the bottom. Don't forget to also add an event listener for resize, as these pixel based values will change if your viewport changes, so your myViewport object would need to be updated accordingly:
function inViewport( element, viewport = { top: 0, bottom: innerHeight } ){
// Get the elements position relative to the viewport
var bb = element.getBoundingClientRect();
// Check if the element is outside the viewport
// Then invert the returned value because you want to know the opposite
return !(bb.top > viewport.bottom || bb.bottom < viewport.top);
}
var myViewport = { top: innerHeight * .4, bottom: innerHeight * .6 };
var myElement = document.querySelector( 'div' );
// Listen for the scroll event
document.addEventListener( 'scroll', event => {
// Check the viewport status
if( inViewport( myElement, myViewport ) ){
myElement.style.opacity = 1;
} else {
myElement.style.opacity = '';
}
})
window.addEventListener( 'resize', event => {
// Update your viewport values
myViewport.top = innerHeight * .4;
myViewport.bottom = innerHeight * .6;
})
body {
height: 400vh;
}
div {
width: 50vw;
height: 50vh;
position: absolute;
top: 125vh;
left: 25vw;
transition: opacity 1s;
opacity: .2;
background: blue;
}
<p>Scroll Down</p>
<div></div>
Try using:
$(window).on('scroll mousewheel', function() {
And surround your function with:
$(document).ready(function(){
});
i tried to solve your problem by your code only. its working fine for me now. plz try this and let me know. also open your browser console to see if there is any js error.
$(window).scroll(function() {
var top_of_element = $("#cont_quote blockquote").offset().top;
var bottom_of_element = $("#cont_quote blockquote").offset().top + $("#cont_quote blockquote").outerHeight();
var bottom_of_screen = $(window).scrollTop() + window.innerHeight;
var top_of_screen = $(window).scrollTop();
if((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)){
$('#cont_quote blockquote').fadeIn(1000);
console.log('if cond');
} else {
$('#cont_quote blockquote').fadeOut(1000);
console.log('else cond');
}
});
I have an issue:
document.querySelector('div').addEventListener('mousedown', function(){
hello = true
document.body.addEventListener('mousemove', function(e){
if (hello) {
document.querySelector('div').style.left = (e.clientX - 12.5) + 'px'
document.querySelector('div').style.top = (e.clientY - 12.5) + 'px'
}
})
this.addEventListener('mouseup', function(e){
hello = false
posY = Math.floor(parseInt(this.style.top))
function Fall() {
posY++
document.querySelector('div').style.top = posY + 'px'
if (posY != parseInt(window.innerHeight) - 25) requestAnimationFrame(Fall)
}
Fall()
})
})
body {
margin:0;
position:absolute;
height:100vh;
width:100vw;
overflow:hidden
}
div {
position:absolute;
height:25px;
width:25px;
border:1px #000 solid;
bottom:0
}
div:hover {
cursor:pointer;
}
<div></div>
In this code (also on jsFiddle, when I drop the div, I want the div to fall, and stop at the ground.
The first time, it works. But then, requestAnimationFrame is faster, it's like the first one isn't done...? And after that, the div didn't stop at the ground :(
Do I have to use setInterval instead of requestAnimationFrame?
Whenever the div is clicked (mousedown) theres another listener assigned. When you stop clicking, these listeners are all executed in order, so at the second click, there will be two loops running, after the third there will be three loops running and so on. You may just assign the listener once:
var hello,posY;
document.querySelector('div').addEventListener('mousedown', function(){
hello = true;
});
document.body.addEventListener('mousemove', function(e){
if (hello) {
document.querySelector('div').style.left = (e.clientX - 12.5) + 'px';
document.querySelector('div').style.top = (e.clientY - 12.5) + 'px';
}
});
document.querySelector('div').addEventListener('mouseup', function(e){
hello = false;
posY = Math.floor(parseInt(this.style.top));
function Fall() {
posY++;
document.querySelector('div').style.top = posY + 'px';
if (posY < parseInt(window.innerHeight) - 25) requestAnimationFrame(Fall);
}
Fall();
});
And please always end a statement with a semicolon...
The problem is that you're adding your mouseup handler every time the div receives a mousedown. You only want to do that once, so move that out of your mousedown handler. So you set up the animation twice on the second call (because both handlers respond), three times on the third (because all three handlers respond), etc. And since multiple handlers are updating poxY, it doesn't stop at the ground anymore because the != check fails for all but one of them. (See further notes under the snippet.)
document.querySelector('div').addEventListener('mousedown', function() {
hello = true
document.body.addEventListener('mousemove', function(e) {
if (hello) {
document.querySelector('div').style.left = (e.clientX - 12.5) + 'px'
document.querySelector('div').style.top = (e.clientY - 12.5) + 'px'
}
})
})
// Moved the below
document.querySelector('div').addEventListener('mouseup', function(e) {
hello = false
posY = Math.floor(parseInt(this.style.top))
function Fall() {
posY++
document.querySelector('div').style.top = posY + 'px'
if (posY != parseInt(window.innerHeight) - 25) requestAnimationFrame(Fall)
}
Fall()
})
body {
margin: 0;
position: absolute;
height: 100vh;
width: 100vw;
overflow: hidden
}
div {
position: absolute;
height: 25px;
width: 25px;
border: 1px #000 solid;
bottom: 0
}
div:hover {
cursor: pointer;
}
<div class="square"></div>
Some other observations:
Your code is falling prey to The Horror of Implicit Globals* — declare your variables
Rather than re-querying the DOM all the time, it would probably be best to query the DOM for the div once, and remember it in a variable
* (disclosure: that's a post on my anemic little blog)
I am trying to make a web app with two boxes, one contained in the other. The user should be able to click and move the inner box, however, the user should not be able to move this box outside the confines of the outer box. The user can move the outer box by dragging the inner box against one of the edges of the outer box. I know how to move the inner box, but the problem is how to move the other box with this restriction. Can anybody help me please? Here is what I did so far:
<!doctype html>
<head>
<title>JavaScript Game</title>
<style>
#container {
height:400px;
width:600px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:green;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:red;
left: 200px;
top: 200px;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="guy"></div>
<script>
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
guy.style.left =guy.offsetLeft + distX + "px";
guy.style.top = guy.offsetTop + distY + "px";
lastX = event.pageX;
lastY = event.pageY;
}
}
</script>
</body>
You could add a check to see if moving the box would break bounds of cont.
try to use getBoundingClientRect()
Check the snippet below for the working code.
View in full screen for best results.
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
guy.style.left =guy.offsetLeft + distX + "px";
guy.style.top = guy.offsetTop + distY + "px";
// ********************************************************************
// get bounding box borders
var contBounds = guy.getBoundingClientRect();
var guyBounds = cont.getBoundingClientRect();
// check bottom bounds
if (contBounds.bottom >= guyBounds.bottom){
cont.style.top = cont.offsetTop + distY + "px";
}
// check top bounds
if (contBounds.top <= guyBounds.top){
cont.style.top = cont.offsetTop + distY + "px";
}
// check left bounds
if (contBounds.left <= guyBounds.left){
cont.style.left = cont.offsetLeft + distX + "px";
}
// check right bounds
if (contBounds.right >= guyBounds.right){
cont.style.left = cont.offsetLeft + distX + "px";
}
// ********************************************************************
lastX = event.pageX;
lastY = event.pageY;
}
}
#container {
height:300px;
width:300px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:#CCC;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:#000;
left: 200px;
top: 200px;
}
<div id="container"></div>
<div id="guy"></div>
try this link to get you started as far as keeping the "guy" inside the "contatiner": http://www.w3schools.com/html/html5_draganddrop.asp
their example shows how you can make an element only drop inside another element.
as far as moving the container...i would think that you could add some if else statements into the moved function that will test the position of the guy against the conatiner's outline and say that when they meet to move the container as well.
i am very new to javascript myself but this is just a suggestion from what i think i understand of it.
I would like to create a div, that is situated beneath a block of content but that once the page has been scrolled enough to contact its top boundary, becomes fixed in place and scrolls with the page.
You could use simply css, positioning your element as fixed:
.fixedElement {
background-color: #c0c0c0;
position:fixed;
top:0;
width:100%;
z-index:100;
}
Edit: You should have the element with position absolute, once the scroll offset has reached the element, it should be changed to fixed, and the top position should be set to zero.
You can detect the top scroll offset of the document with the scrollTop function:
$(window).scroll(function(e){
var $el = $('.fixedElement');
var isPositionFixed = ($el.css('position') == 'fixed');
if ($(this).scrollTop() > 200 && !isPositionFixed){
$el.css({'position': 'fixed', 'top': '0px'});
}
if ($(this).scrollTop() < 200 && isPositionFixed){
$el.css({'position': 'static', 'top': '0px'});
}
});
When the scroll offset reached 200, the element will stick to the top of the browser window, because is placed as fixed.
You've seen this example on Google Code's issue page and (only recently) on Stack Overflow's edit page.
CMS's answer doesn't revert the positioning when you scroll back up. Here's the shamelessly stolen code from Stack Overflow:
function moveScroller() {
var $anchor = $("#scroller-anchor");
var $scroller = $('#scroller');
var move = function() {
var st = $(window).scrollTop();
var ot = $anchor.offset().top;
if(st > ot) {
$scroller.css({
position: "fixed",
top: "0px"
});
} else {
$scroller.css({
position: "relative",
top: ""
});
}
};
$(window).scroll(move);
move();
}
<div id="sidebar" style="width:270px;">
<div id="scroller-anchor"></div>
<div id="scroller" style="margin-top:10px; width:270px">
Scroller Scroller Scroller
</div>
</div>
<script type="text/javascript">
$(function() {
moveScroller();
});
</script>
And a simple live demo.
A nascent, script-free alternative is position: sticky, which is supported in Chrome, Firefox, and Safari. See the article on HTML5Rocks and demo, and Mozilla docs.
As of January 2017 and the release of Chrome 56, most browsers in common use support the position: sticky property in CSS.
#thing_to_stick {
position: sticky;
top: 0px;
}
does the trick for me in Firefox and Chrome.
In Safari you still need to use position: -webkit-sticky.
Polyfills are available for Internet Explorer and Edge; https://github.com/wilddeer/stickyfill seems to be a good one.
And here's how without jquery (UPDATE: see other answers where you can now do this with CSS only)
var startProductBarPos=-1;
window.onscroll=function(){
var bar = document.getElementById('nav');
if(startProductBarPos<0)startProductBarPos=findPosY(bar);
if(pageYOffset>startProductBarPos){
bar.style.position='fixed';
bar.style.top=0;
}else{
bar.style.position='relative';
}
};
function findPosY(obj) {
var curtop = 0;
if (typeof (obj.offsetParent) != 'undefined' && obj.offsetParent) {
while (obj.offsetParent) {
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
curtop += obj.offsetTop;
}
else if (obj.y)
curtop += obj.y;
return curtop;
}
* {margin:0;padding:0;}
.nav {
border: 1px red dashed;
background: #00ffff;
text-align:center;
padding: 21px 0;
margin: 0 auto;
z-index:10;
width:100%;
left:0;
right:0;
}
.header {
text-align:center;
padding: 65px 0;
border: 1px red dashed;
}
.content {
padding: 500px 0;
text-align:center;
border: 1px red dashed;
}
.footer {
padding: 100px 0;
text-align:center;
background: #777;
border: 1px red dashed;
}
<header class="header">This is a Header</header>
<div id="nav" class="nav">Main Navigation</div>
<div class="content">Hello World!</div>
<footer class="footer">This is a Footer</footer>
I had the same problem as you and ended up making a jQuery plugin to take care of it. It actually solves all the problems people have listed here, plus it adds a couple of optional features too.
Options
stickyPanelSettings = {
// Use this to set the top margin of the detached panel.
topPadding: 0,
// This class is applied when the panel detaches.
afterDetachCSSClass: "",
// When set to true the space where the panel was is kept open.
savePanelSpace: false,
// Event fires when panel is detached
// function(detachedPanel, panelSpacer){....}
onDetached: null,
// Event fires when panel is reattached
// function(detachedPanel){....}
onReAttached: null,
// Set this using any valid jquery selector to
// set the parent of the sticky panel.
// If set to null then the window object will be used.
parentSelector: null
};
https://github.com/donnyv/sticky-panel
demo: http://htmlpreview.github.io/?https://github.com/donnyv/sticky-panel/blob/master/jquery.stickyPanel/Main.htm
The simplest solution (without js) :
demo
.container {
position: relative;
}
.sticky-div {
position: sticky;
top: 0;
}
<div class="container">
<h1>
relative container & sticky div
</h1>
<div class="sticky-div"> this row is sticky</div>
<div>
content
</div>
</div>
This is how i did it with jquery. This was all cobbled together from various answers on stack overflow. This solution caches the selectors for faster performance and also solves the "jumping" issue when the sticky div becomes sticky.
Check it out on jsfiddle: http://jsfiddle.net/HQS8s/
CSS:
.stick {
position: fixed;
top: 0;
}
JS:
$(document).ready(function() {
// Cache selectors for faster performance.
var $window = $(window),
$mainMenuBar = $('#mainMenuBar'),
$mainMenuBarAnchor = $('#mainMenuBarAnchor');
// Run this on scroll events.
$window.scroll(function() {
var window_top = $window.scrollTop();
var div_top = $mainMenuBarAnchor.offset().top;
if (window_top > div_top) {
// Make the div sticky.
$mainMenuBar.addClass('stick');
$mainMenuBarAnchor.height($mainMenuBar.height());
}
else {
// Unstick the div.
$mainMenuBar.removeClass('stick');
$mainMenuBarAnchor.height(0);
}
});
});
As Josh Lee and Colin 't Hart have said, you could optionally just use position: sticky; top: 0; applying to the div that you want the scrolling at...
Plus, the only thing you will have to do is copy this into the top of your page or format it to fit into an external CSS sheet:
<style>
#sticky_div's_name_here { position: sticky; top: 0; }
</style>
Just replace #sticky_div's_name_here with the name of your div, i.e. if your div was <div id="example"> you would put #example { position: sticky; top: 0; }.
Here is another option:
JAVASCRIPT
var initTopPosition= $('#myElementToStick').offset().top;
$(window).scroll(function(){
if($(window).scrollTop() > initTopPosition)
$('#myElementToStick').css({'position':'fixed','top':'0px'});
else
$('#myElementToStick').css({'position':'absolute','top':initTopPosition+'px'});
});
Your #myElementToStick should start with position:absolute CSS property.
Here's one more version to try for those having issues with the others. It pulls together the techniques discussed in this duplicate question, and generates the required helper DIVs dynamically so no extra HTML is required.
CSS:
.sticky { position:fixed; top:0; }
JQuery:
function make_sticky(id) {
var e = $(id);
var w = $(window);
$('<div/>').insertBefore(id);
$('<div/>').hide().css('height',e.outerHeight()).insertAfter(id);
var n = e.next();
var p = e.prev();
function sticky_relocate() {
var window_top = w.scrollTop();
var div_top = p.offset().top;
if (window_top > div_top) {
e.addClass('sticky');
n.show();
} else {
e.removeClass('sticky');
n.hide();
}
}
w.scroll(sticky_relocate);
sticky_relocate();
}
To make an element sticky, do:
make_sticky('#sticky-elem-id');
When the element becomes sticky, the code manages the position of the remaining content to keep it from jumping into the gap left by the sticky element. It also returns the sticky element to its original non-sticky position when scrolling back above it.
My solution is a little verbose, but it handles variable positioning from the left edge for centered layouts.
// Ensurs that a element (usually a div) stays on the screen
// aElementToStick = The jQuery selector for the element to keep visible
global.makeSticky = function (aElementToStick) {
var $elementToStick = $(aElementToStick);
var top = $elementToStick.offset().top;
var origPosition = $elementToStick.css('position');
function positionFloater(a$Win) {
// Set the original position to allow the browser to adjust the horizontal position
$elementToStick.css('position', origPosition);
// Test how far down the page is scrolled
var scrollTop = a$Win.scrollTop();
// If the page is scrolled passed the top of the element make it stick to the top of the screen
if (top < scrollTop) {
// Get the horizontal position
var left = $elementToStick.offset().left;
// Set the positioning as fixed to hold it's position
$elementToStick.css('position', 'fixed');
// Reuse the horizontal positioning
$elementToStick.css('left', left);
// Hold the element at the top of the screen
$elementToStick.css('top', 0);
}
}
// Perform initial positioning
positionFloater($(window));
// Reposition when the window resizes
$(window).resize(function (e) {
positionFloater($(this));
});
// Reposition when the window scrolls
$(window).scroll(function (e) {
positionFloater($(this));
});
};
Here is an extended version to Josh Lee's answer. If you want the div to be on sidebar to the right, and float within a range (i.e., you need to specify top and bottom anchor positions). It also fixes a bug when you view this on mobile devices (you need to check left scroll position otherwise the div will move off screen).
function moveScroller() {
var move = function() {
var st = $(window).scrollTop();
var sl = $(window).scrollLeft();
var ot = $("#scroller-anchor-top").offset().top;
var ol = $("#scroller-anchor-top").offset().left;
var bt = $("#scroller-anchor-bottom").offset().top;
var s = $("#scroller");
if(st > ot) {
if (st < bt - 280) //280px is the approx. height for the sticky div
{
s.css({
position: "fixed",
top: "0px",
left: ol-sl
});
}
else
{
s.css({
position: "fixed",
top: bt-st-280,
left: ol-sl
});
}
} else {
s.css({
position: "relative",
top: "",
left: ""
});
}
};
$(window).scroll(move);
move();
}
I came across this when searching for the same thing. I know it's an old question but I thought I'd offer a more recent answer.
Scrollorama has a 'pin it' feature which is just what I was looking for.
http://johnpolacek.github.io/scrollorama/
The info provided to answer this other question may be of help to you, Evan:
Check if element is visible after scrolling
You basically want to modify the style of the element to set it to fixed only after having verified that the document.body.scrollTop value is equal to or greater than the top of your element.
The accepted answer works but doesn't move back to previous position if you scroll above it. It is always stuck to the top after being placed there.
$(window).scroll(function(e) {
$el = $('.fixedElement');
if ($(this).scrollTop() > 42 && $el.css('position') != 'fixed') {
$('.fixedElement').css( 'position': 'fixed', 'top': '0px');
} else if ($(this).scrollTop() < 42 && $el.css('position') != 'relative') {
$('.fixedElement').css( 'relative': 'fixed', 'top': '42px');
//this was just my previous position/formating
}
});
jleedev's response whould work, but I wasn't able to get it to work. His example page also didn't work (for me).
You can add 3 extra rows so when the user scroll back to the top, the div will stick on its old place:
Here is the code:
if ($(this).scrollTop() < 200 && $el.css('position') == 'fixed'){
$('.fixedElement').css({'position': 'relative', 'top': '200px'});
}
I have links setup in a div so it is a vertical list of letter and number links.
#links {
float:left;
font-size:9pt;
margin-left:0.5em;
margin-right:1em;
position:fixed;
text-align:center;
width:0.8em;
}
I then setup this handy jQuery function to save the loaded position and then change the position to fixed when scrolling beyond that position.
NOTE: this only works if the links are visible on page load!!
var listposition=false;
jQuery(function(){
try{
///// stick the list links to top of page when scrolling
listposition = jQuery('#links').css({'position': 'static', 'top': '0px'}).position();
console.log(listposition);
$(window).scroll(function(e){
$top = $(this).scrollTop();
$el = jQuery('#links');
//if(typeof(console)!='undefined'){
// console.log(listposition.top,$top);
//}
if ($top > listposition.top && $el.css('position') != 'fixed'){
$el.css({'position': 'fixed', 'top': '0px'});
}
else if ($top < listposition.top && $el.css('position') == 'fixed'){
$el.css({'position': 'static'});
}
});
} catch(e) {
alert('Please vendor admin#mydomain.com (Myvendor JavaScript Issue)');
}
});
In javascript you can do:
var element = document.getElementById("myid");
element.style.position = "fixed";
element.style.top = "0%";
Here's an example that uses jquery-visible plugin: http://jsfiddle.net/711p4em4/.
HTML:
<div class = "wrapper">
<header>Header</header>
<main>
<nav>Stick to top</nav>
Content
</main>
<footer>Footer</footer>
</div>
CSS:
* {
margin: 0;
padding: 0;
}
body {
background-color: #e2e2e2;
}
.wrapper > header,
.wrapper > footer {
font: 20px/2 Sans-Serif;
text-align: center;
background-color: #0040FF;
color: #fff;
}
.wrapper > main {
position: relative;
height: 500px;
background-color: #5e5e5e;
font: 20px/500px Sans-Serif;
color: #fff;
text-align: center;
padding-top: 40px;
}
.wrapper > main > nav {
position: absolute;
top: 0;
left: 0;
right: 0;
font: 20px/2 Sans-Serif;
color: #fff;
text-align: center;
background-color: #FFBF00;
}
.wrapper > main > nav.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
}
JS (include jquery-visible plugin):
(function($){
/**
* Copyright 2012, Digital Fusion
* Licensed under the MIT license.
* http://teamdf.com/jquery-plugins/license/
*
* #author Sam Sehnert
* #desc A small plugin that checks whether elements are within
* the user visible viewport of a web browser.
* only accounts for vertical position, not horizontal.
*/
var $w = $(window);
$.fn.visible = function(partial,hidden,direction){
if (this.length < 1)
return;
var $t = this.length > 1 ? this.eq(0) : this,
t = $t.get(0),
vpWidth = $w.width(),
vpHeight = $w.height(),
direction = (direction) ? direction : 'both',
clientSize = hidden === true ? t.offsetWidth * t.offsetHeight : true;
if (typeof t.getBoundingClientRect === 'function'){
// Use this native browser method, if available.
var rec = t.getBoundingClientRect(),
tViz = rec.top >= 0 && rec.top < vpHeight,
bViz = rec.bottom > 0 && rec.bottom <= vpHeight,
lViz = rec.left >= 0 && rec.left < vpWidth,
rViz = rec.right > 0 && rec.right <= vpWidth,
vVisible = partial ? tViz || bViz : tViz && bViz,
hVisible = partial ? lViz || rViz : lViz && rViz;
if(direction === 'both')
return clientSize && vVisible && hVisible;
else if(direction === 'vertical')
return clientSize && vVisible;
else if(direction === 'horizontal')
return clientSize && hVisible;
} else {
var viewTop = $w.scrollTop(),
viewBottom = viewTop + vpHeight,
viewLeft = $w.scrollLeft(),
viewRight = viewLeft + vpWidth,
offset = $t.offset(),
_top = offset.top,
_bottom = _top + $t.height(),
_left = offset.left,
_right = _left + $t.width(),
compareTop = partial === true ? _bottom : _top,
compareBottom = partial === true ? _top : _bottom,
compareLeft = partial === true ? _right : _left,
compareRight = partial === true ? _left : _right;
if(direction === 'both')
return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop)) && ((compareRight <= viewRight) && (compareLeft >= viewLeft));
else if(direction === 'vertical')
return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop));
else if(direction === 'horizontal')
return !!clientSize && ((compareRight <= viewRight) && (compareLeft >= viewLeft));
}
};
})(jQuery);
$(function() {
$(window).scroll(function() {
$(".wrapper > header").visible(true) ?
$(".wrapper > main > nav").removeClass("fixed") :
$(".wrapper > main > nav").addClass("fixed");
});
});
I used some of the work above to create this tech. I improved it a bit and thought I would share my work. Hope this helps.
jsfiddle Code
function scrollErrorMessageToTop() {
var flash_error = jQuery('#flash_error');
var flash_position = flash_error.position();
function lockErrorMessageToTop() {
var place_holder = jQuery("#place_holder");
if (jQuery(this).scrollTop() > flash_position.top && flash_error.attr("position") != "fixed") {
flash_error.css({
'position': 'fixed',
'top': "0px",
"width": flash_error.width(),
"z-index": "1"
});
place_holder.css("display", "");
} else {
flash_error.css('position', '');
place_holder.css("display", "none");
}
}
if (flash_error.length > 0) {
lockErrorMessageToTop();
jQuery("#flash_error").after(jQuery("<div id='place_holder'>"));
var place_holder = jQuery("#place_holder");
place_holder.css({
"height": flash_error.height(),
"display": "none"
});
jQuery(window).scroll(function(e) {
lockErrorMessageToTop();
});
}
}
scrollErrorMessageToTop();
This is a little bit more dynamic of a way to do the scroll. It does need some work and I will at some point turn this into a pluging but but this is what I came up with after hour of work.
Not an exact solution but a great alternative to consider
this CSS ONLY Top of screen scroll bar. Solved all the problem with ONLY CSS, NO JavaScript, NO JQuery, No Brain work (lol).
Enjoy my fiddle :D all the codes are included in there :)
CSS
#menu {
position: fixed;
height: 60px;
width: 100%;
top: 0;
left: 0;
border-top: 5px solid #a1cb2f;
background: #fff;
-moz-box-shadow: 0 2px 3px 0px rgba(0, 0, 0, 0.16);
-webkit-box-shadow: 0 2px 3px 0px rgba(0, 0, 0, 0.16);
box-shadow: 0 2px 3px 0px rgba(0, 0, 0, 0.16);
z-index: 999999;
}
.w {
width: 900px;
margin: 0 auto;
margin-bottom: 40px;
}<br type="_moz">
Put the content long enough so you can see the effect here :)
Oh, and the reference is in there as well, for the fact he deserve his credit
CSS ONLY Top of screen scroll bar
sticky till the footer hits the div:
function stickyCostSummary() {
var stickySummary = $('.sticky-cost-summary');
var scrollCostSummaryDivPosition = $(window).scrollTop();
var footerHeight = $('#footer').height();
var documentHeight = $(document).height();
var costSummaryHeight = stickySummary.height();
var headerHeight = 83;
var footerMargin = 10;
var scrollHeight = 252;
var footerPosition = $('#footer').offset().top;
if (scrollCostSummaryDivPosition > scrollHeight && scrollCostSummaryDivPosition <= (documentHeight - footerHeight - costSummaryHeight - headerHeight - footerMargin)) {
stickySummary.removeAttr('style');
stickySummary.addClass('fixed');
} else if (scrollCostSummaryDivPosition > (documentHeight - footerHeight - costSummaryHeight - headerHeight - footerMargin)) {
stickySummary.removeClass('fixed');
stickySummary.css({
"position" : "absolute",
"top" : (documentHeight - footerHeight - costSummaryHeight - headerHeight - footerMargin - scrollHeight) + "px"
});
} else {
stickySummary.removeClass('fixed');
stickySummary.css({
"position" : "absolute",
"top" : "0"
});
}
}
$window.scroll(stickyCostSummary);