DIV stick to top till pushed offscreen by next DIV - javascript

I am trying to get a similar effect to the Instagram timeline on a project I am doing. My HTML is:
<div class="item">
<div class="title">
<h1>Some title</h1>
<span>Time here</span>
</div>
<div class="content"></div>
</div>
Which is repeated many times down the page, and my CSS is:
* {
margin: 0;
padding: 0;
}
.item {
width: 100%;
background-color: red;
}
.item h1, span{
padding: 2px 0 5px 5px;
}
.item span{
display: inline;
}
.title {
background-color: blue;
}
.content {
height: 200px;
background-color: green;
}
What I want to happen is when a user scrolls, <div class="title"> sticks to the top when a user is scrolling, but when the next <div class="title"> comes up it 'pushes' the previous one off screen and then fixes it's self to the top.
Screenshots:
Picture 1 - Look at the two headers, one for withhearts the other for brenton_clarke.
Picture 2 - brenton_clarke's header has reached the bottom of withheart's
Picture 3 - brenton_clarke's header is pushing withheart's offscreen
Picture 4 - brenton_clarke's headers is now stuck to the top till pauloctavious pushes it off
My Fiddle
Can anyone give me some help with this?
With the link suggested below I was able to get it sort-of working, but not great: http://jsfiddle.net/reb6X/1/
Having modified the jQuery to use .html() rather than .text() it works not to badly now: http://jsfiddle.net/reb6X/2/

You might want to check out this jsfiddle.
http://jsfiddle.net/kennis/JTvFZ/
I think it could get you in the right direction.
// Index of the currently 'active' section
var activeCache = null;
// Actual rendered height of a header element
var cloneHeight = function(){
var $clone = $('<div class="clone"></div>').appendTo('body'),
cloneHeight = $clone.outerHeight();
$clone.remove();
return cloneHeight;
}();
// Top offsets of each header
var offsets = [];
// Figure out which section is 'active'
var activeHeaderIndex = function(){
var scrollTop = document.body.scrollTop;
for ( var i = 0; i < offsets.length; i++ )
if ( offsets[i] - cloneHeight > scrollTop )
return Math.max( i - 1, 0 );
}
// Build the 'offsets' array
$('.header').each(function(i, obj){
offsets.push( $(this).offset().top );
});
// Listen to scroll events
$(window).on('scroll', function(){
var active = activeHeaderIndex(),
scroll = document.body.scrollTop,
clone = $('.clone').length,
$active = $('.header').eq(active),
prevTitle = $('.header').eq(active - 1).text(),
title = $active.text(),
$fixed = $('.fixed');
// Hide fixed header
if ( offsets[active] > scroll ){
if ( !clone ){
$('.header').eq(0).hide();
$('<li class="clone">' + prevTitle + '</li>').insertBefore($active);
}
$fixed.hide();
// Show fixed header
} else {
if ( clone ){
$('.header').eq(0).show();
$('.clone').remove();
}
$fixed.show();
}
// If we're not changing headers, exit
if ( active == activeCache ) return;
// Update active index
activeCache = active;
// Remove old fixed header (if any)
$('.fixed').remove();
// Add a new fixed header
$fixed = $('<div class="fixed">' + title + '</div>').appendTo('body');
}).trigger('scroll');

Related

How to convert the following jQuery to Javascript for ReactJS?

I have a ReactJS project and I've been advised not to use jQuery for various reasons, so I'm attempting to convert the following jQuery to JavaScript -- it smoothly changes background color while scrolling the page:
$( window ).ready(function() {
var wHeight = $(window).height();
$('.slide')
.height(wHeight)
.scrollie({
scrollOffset : -50,
scrollingInView : function(elem) {
var bgColor = elem.data('background');
$('body').css('background-color', bgColor);
}
});
});
CSS:
* { box-sizing: border-box }
body {
font-family: 'Coming Soon', cursive;
transition: background 1s ease;
background: #3498db;
}
p {
color: #ecf0f1;
font-size: 2em;
text-align: center;
}
a {
text-decoration: none;
}
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/2542/jquery.scrollie.min_1.js"></script>
<div class="main-wrapper">
<div class="slide slide-one" data-background="#3498db">
<p>Title</p>
<center>Go To Green.</center>
</div>
<div class="slide slide-two" data-background="#27ae60">
<a name="green">
<p>Green area</p>
<center>Go To Red.</center>
</a>
</div>
<div class="slide slide-three" data-background="#e74c3c">
<a name="red">
<p>Red area</p>
<center>Page over. Hope that was helpful :)</center>
</a>
</div>
But how can I do the conversion to JavaScript to fit the ReactJS project?
Thank you in advance and will be sure to accept/upvote answer
Changing from JQuery to JavaScript is always possible. Because JQuery builds on JavaScript. Most of the time ( like in your case ) it's not even that much work.
I've not changed your CSS or HTML. So this is just some new JavaScript. However you should put this script at the end of your website.
(function() { // create an own scope and run when everything is loaded
// collect all the slides in this array and apply the correct height
var slides = document.getElementsByClassName('slide')
for (slide of slides) slide.style.height = window.innerHeight + 'px'
// use the native scroll event
document.addEventListener("scroll", function() {
// how much have we scrolled already
var currentOffset = document.documentElement.scrollTop || document.body.scrollTop
// now check for all slides if they are in view (only one will be)
for (slide of slides) {
// 200 is how much before the top the color should change
var top = slide.getBoundingClientRect().top + currentOffset - 200
var bottom = top + slide.offsetHeight
// check if the current slide is in view
if (currentOffset >= top && currentOffset <= bottom) {
// set the new color, the smooth transition comes from the CSS tag
// CSS: transition: background 1s ease;
document.body.style.background = slide.dataset.background
break
}
}
})
}())
Additionally you might want to listen on resize event, because as of now when you resize the window will look a bit off ( this replaces the 5 line of the code above)
function setSize() {
for (slide of slides) slide.style.height = window.innerHeight + 'px'
}
window.addEventListener("resize", setSize)
setSize()
Solution
(function() {
var slides = document.getElementsByClassName('slide')
function setSize() {
for (slide of slides) slide.style.height = window.innerHeight + 'px'
}
window.addEventListener("resize", setSize)
setSize()
document.addEventListener("scroll", function() {
var currentOffset = document.documentElement.scrollTop || document.body.scrollTop
for (slide of slides) {
// 100 is how much before the top the color should change
var top = slide.getBoundingClientRect().top + currentOffset - 100
var bottom = top + slide.offsetHeight
if (currentOffset >= top && currentOffset <= bottom) {
document.body.style.background = slide.dataset.background
break
}
}
})
}())
body {
font-family: 'Coming Soon', cursive;
transition: background 1s ease;
background: #3498db;
}
p {
color: #ecf0f1;
font-size: 2em;
text-align: center;
}
a { text-decoration: none;
}
<div class="main-wrapper">
<div class="slide slide-one" data-background="#3498db">
<p>Title</p>
<center>Go To Green.</center>
</div>
<div class="slide slide-two" data-background="#27ae60">
<a name="green">
<p>Green area</p>
<center>Go To Red.</center>
</a>
</div>
<div class="slide slide-three" data-background="#e74c3c">
<a name="red">
<p>Red area</p>
<center>Page over. Hope that was helpful :)</center>
</a>
</div>
The plugin you are referring to links an example from their README that includes a link to a newer version that makes use of this other plugin that does not use jQuery and in fact does what you want it to do, I think. It is called in-view and it looks very good to me (both the functionality and the code).
Its usage is very similar to what you are doing currently. From the newer example linked above:
var $target = $('.wrapper');
inView('.section').on('enter', function(el){
var color = $(el).attr('data-background-color');
$target.css('background-color', color );
});
I do realize that I am in a way not answering the question, because this is not a jQuery-to-vanilla-JS guide, but I do think that it helps to know that somebody already did it for you.
Should give you some idea.
var wHeight = window.innerHeight;
To select elements in javascript:
on browser open inspect element, console tab and type:
document.get
and it gives you hint what to get
To get style of element:
var elem1 = document.getElementById("elemId");
var style = window.getComputedStyle(elem1, null);
To set Property:
document.body.style.setProperty(height`, wHeight +"px");
Below is some modifications that you can make, As people commented I am not trying to teach you how to's but want to give you some start:
// $( window ).ready(function() { //remove this
var wHeight = $(window).height(); // USe this instead: var wHeight = window.innerHeight;
$('.slide') //instead of selecting with jquery "$('.slide')" select with: Javascript var x = document.getElementsByClassName("example") ;
.height(wHeight) // Here you are chaining methods, read this article https://schier.co/blog/2013/11/14/method-chaining-in-javascript.html
.scrollie({
scrollOffset : -50,
scrollingInView : function(elem) {
var bgColor = elem.data('background'); //var bgColor = window.getComputedStyle(elem, null);
$('body').css('background-color', bgColor); //document.body.style.background = bgColor;
}
});
// }); //remove this
For .... in
var t = document.getElementsByClassName('slide')
console.log(typeof t) //object
for (var prop in t) {
console.log('t.' + prop, '=', t[prop]);
// output is index.html:33 t.0 = <div class=​"slide slide-one" data- background=​"#3498db">​…​</div>​
// output is index.html:33 t.1 = <div class=​"slide slide-two" data- background=​"#27ae60">​…​</div>​
// output is index.html:33 t.2 = <div class=​"slide slide-three" data-background=​"#e74c3c">​…​</div>​<a name=​"red">​…​</a>​</div>​
// output is index.html:33 t.length = 3
// output is index.html:33 t.item = function item() { [native code] }
// output is index.html:33 t.namedItem = function namedItem() { [native code] }
}

Finding position of element within scrollable div

I have these "pages" aka div's inside a scrollable container. On command, I am trying to find out what part of the div in question, is touching the top of .pageContent.
So for example, right when the page loads, no part of #page_1 is touching the top of pageContent, but as I scroll down. #page_1 hits the top of .pageContent and I now want to figure out where that is.
I know I can get the position of .pageContent using $("#pageContent").scrollTop() but these page's could be different sizes and I am not sure how to go about figuring it out.
Could anyone put me in the right direction?
jsfiddle
HTML
<div id="pageContent">
<div id="page_1" class="content"></div>
<div id="page_2" class="content"></div>
<div id="page_3" class="content"></div>
</div>
CSS
#pageContent {
overflow: auto;
width:500px;
height:300px;
padding:10px;
border:1px solid black;
background-color:grey;
}
.content {
height:400px;
width:300px;
margin:0 auto;
background-color:red;
margin-bottom:10px;
}
You can use the jQuery .position() function to compute where each page is in relation to the top of the container. See this Fiddle.
For example, for #page_1,
var page1 = $('#page_1');
$('#pageContent').scroll(function() {
// page1.position().top gives the position of page_1 relative to the
// top of #pageContent
});
ScrollTop can be used, be I wouldn't recommend it.
Attach a scroll event to your main div and listener for all the objects inside:
$('#pageContent').scroll(function(){
var pages = $("#pageContent > .content");
for (var i = 0; i < pages.length; i++)
{
if ($(pages[i]).position().top < 0 && ( $(pages[i]).position().top + $(pages[i]).outerHeight() ) > 0)
{
var outerHeight = $(pages[i]).outerHeight();
var pixels = (outerHeight - (outerHeight + $(pages[i]).position().top));
console.log("These pixels are in view between: " + pixels + " and " + outerHeight );
}
}
})
Every time the div scroll a loop is performed checking the position of all elements. If the elements scroll out of view a the top the if is triggered, calculating the remaining visible pixels of the page currently visible.
This uses jQuery's: position() and outerHeight() and JavaScript's native offsetTop.
http://jsfiddle.net/q5aaLo9L/4/
I tried something like this
$(document).ready(function () {
var divs = $('.content').map(function (i, el) {
return $(el).offset().top - $(el).parent().offset().top;
});
$('#pageContent').scroll(function () {
var index = findIndex($(this).scrollTop(), divs) - 1;
if (index > -1) {
console.log($(this).children().eq(index).attr('id'));
} else {
console.log('outside');
}
});
});
function findIndex(pos, divs) {
return (divs.filter(function (el, et) {
return et <= pos
}).length);
}
It's not super clean code because I had to do it quickly.
DEMO
I hope this helps
I mocked this up, it uses JQuery's each() function to iterate through the pages and return the information of the page that has breached the top of the box.
I wasn't sure from your question exactly what you wanted returned, so I got it to return either the percentage of the page that has cleared the top border, the position (as negative value of pixels) of the top of the "page " in relation to the content container, and also just the ID of that div.
var getCurrentPage = function(){
var page;
var position;
var percentageRead;
$('.content').each(function(){
if($(this).position().top <= 0){
page = $(this);
position = $(this).position().top;
}
});
percentageRead = ((position *-1)/ $(page).height()* 100);
console.log(page.attr('id'));
console.log(position);
console.log(percentageRead + '%');
}
$('#pageContent').on('scroll', getCurrentPage);
You could fire this on any event but I used scroll to build it.

Filling div using letter-spacing

The problem I'm having is filling a div with text using letter-spacing. The main issue is, I don't know the width of the div.
First I was thinking using, text-align= justify, but since that I've been running in the dark and got no clue to how to solve this. I'm guessing some scripting magic might do the trick.
An imgur link giving you an idea what I mean:
<div id="container">
<h1>Sample</h1>
<p>Another even longer sample text</p>
</div>
Here is a link showcasing an example; JSfiddle.
Based the comment of the poster it seems JavaScript is no problem. Here's a possible approach to solve the problem with jQuery:
JSFiddle 1
function dynamicSpacing(full_query, parent_element) {
$(full_query).css('letter-spacing', 0);
var content = $(full_query).html();
var original = content;
content = content.replace(/(\w|\s)/g, '<span>$1</span>');
$(full_query).html(content);
var letter_width = 0;
var letters_count = 0;
$(full_query + ' span').each(function() {
letter_width += $(this).width();
letters_count++;
});
var h1_width = $(parent_element).width();
var spacing = (h1_width - letter_width) / (letters_count - 1);
$(full_query).html(original);
$(full_query).css('letter-spacing', spacing);
}
$(document).ready(function() {
// Initial
dynamicSpacing('#container h1', '#container');
// Refresh
$(window).resize(function() {
dynamicSpacing('#container h1', '#container');
});
});
Update
Small tweak for when the wrapper gets too small: JSFiddle 2
Another solution if you don't have to be semantic (because you will get many spans), I mean if you need only the visual result, is to use flexbox.
So you have your <div id="#myText">TEXT 1</div>
We need to get this:
<div id="#myText">
<span>T</span>
<span>E</span>
<span>X</span>
<span>T</span>
<span> </span>
<span>1</span>
</div>
So then you can apply CSS:
#myText {
display: flex;
flex-direction: row;
justify-content: space-between;
}
In order to transform the text to span you can use jQuery or whatever. Here with jQuery:
var words = $('#myText').text().split("");
$('#myText').empty();
$.each(words, function(i, v) {
if(v===' '){
$('#myText').append('<span> </span>');
} else {
$('#myText').append($("<span>").text(v));
}
});
For better results remove put letter-spacing: 0 into #myText so any extra spacing will be applied.
This is obviously evil, but since there is no straight forward way to do it with just css, you could do: demo
HTML:
<div>text</div>
CSS:
div, table {
background: yellow;
}
table {
width: 100%;
}
td {
text-align: center;
}
JS:
var text = jQuery("div").text();
var table = jQuery("<table><tr></tr></table>").get(0);
var row = table.rows[0];
for (var i = 0; i < text.length; i++) {
var cell = row.insertCell(-1);
jQuery(cell).text(text[i]);
}
jQuery("div").replaceWith(table);
This may help:
function fill(target) {
var elems = target.children();
$.each(elems, function(i,e) {
var x = 1;
var s = parseInt($(e).css('letter-spacing').replace('px',''));
while(x == 1) {
if($(e).width() <= target.width() - 10) {
s++;
$(e).css('letter-spacing', s+'px');
} else {
x = 0;
}
}
});
}
fill($('#test'));
Note: If letter spacing is : 0 then you don't have to use replace method. Or you can add letter-spacing:1px; to your css file.
For avoiding overflow, always give minus number to parent element's height for correct work.
An other approach I wrote for this question Stretch text to fit width of div. It calculates and aplies letter-spacing so the text uses the whole available space in it's container on page load and on window resize :
DEMO
HTML :
<div id="container">
<h1 class="stretch">Sample</h1>
<p class="stretch">Another even longer sample text</p>
</div>
jQuery :
$.fn.strech_text = function(){
var elmt = $(this),
cont_width = elmt.width(),
txt = elmt.text(),
one_line = $('<span class="stretch_it">' + txt + '</span>'),
nb_char = elmt.text().length,
spacing = cont_width/nb_char,
txt_width;
elmt.html(one_line);
txt_width = one_line.width();
if (txt_width < cont_width){
var char_width = txt_width/nb_char,
ltr_spacing = spacing - char_width + (spacing - char_width)/nb_char ;
one_line.css({'letter-spacing': ltr_spacing});
} else {
one_line.contents().unwrap();
elmt.addClass('justify');
}
};
$(document).ready(function () {
$('.stretch').each(function(){
$(this).strech_text();
});
$(window).resize(function () {
$('.stretch').each(function(){
$(this).strech_text();
});
});
});
CSS :
body {
padding: 130px;
}
#container {
width: 100%;
background: yellow;
}
.stretch_it{
white-space: nowrap;
}
.justify{
text-align:justify;
}

How to make text appear on scroll in html

Hello, I want a certain text to appear when I scroll past it or when I scroll until the point where the text is. The effect when appearing should be somewhat like the first effect on the top of the website http://namanyayg.com/.
I want the effect in minimal code with pure CSS and JS i.e no jQuery.
I was thinking that maybe I would use something like a display:none property for a span and then when you scroll past it the display becomes block but I dont know how to trigger the effect using javascript.
Any help would be appreciated.
First wrap whatever your text or content that you want to show on scroll, in one div so that you can show hide the div depending upon the scroll. Write two classes for your target div.
Your CSS:
/*Use this class when you want your content to be hidden*/
.BeforeScroll
{
height: 100px; /*Whatever you want*/
width: 100%; /*Whatever you want*/
.
.
display: none;
}
/*Use this class when you want your content to be shown after some scroll*/
.AfterScroll
{
height: 100px; /*Whatever you want*/
width: 100%; /*Whatever you want*/
.
.
display: block;
}
Your HTML:
<!--Set class BeforeScoll to your target div-->
<div id = "divToShowHide" class = "BeforeScroll">Content you want to show hide on scroll</div>
Your Script:
<!--include these script in head section or wherever you want-->
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/jquery-ui.min.js" type="text/javascript"></script>
<script type = "text/javascript">
$(document).ready(function(){
//Take your div into one js variable
var div = $("#divToShowHide");
//Take the current position (vertical position from top) of your div in the variable
var pos = div.position();
//Now when scroll event trigger do following
$(window).scroll(function () {
var windowpos = $(window).scrollTop();
//Now if you scroll more than 100 pixels vertically change the class to AfterScroll
// I am taking 100px scroll, you can take whatever you need
if (windowpos >= (pos.top - 100)) {
div.addClass("AfterScroll");
}
//If scroll is less than 100px, remove the class AfterScroll so that your content will be hidden again
else {
s.removeClass("AfterScroll");
}
//Note: If you want the content should be shown always once you scroll and do not want to hide it again when go to top agian, no need to write the else part
});
});
</script>
Hope it will solve your problem.
I would recommend this plugin
http://johnpolacek.github.io/superscrollorama/
Edit:
I don't know how no one noticed that the solution had to be made without using external libraries like jQuery. However, the solution is extremely easy with basic functionality. Find it here
HTML:
<div id="parent-div">
<div id="child-div">
Psst .. I am here!!
</div>
</div>
CSS:
#parent-div
{
position:relative;
height:3000px;
width:300px;
background-color:red;
}
#child-div
{
color:white;
position:relative;
top:1000px;
width:300px;
display:none;
text-align:center;
}
JS:
var body=document.getElementsByTagName("body")[0];
var parent=document.getElementById("parent-div");
var child=document.getElementById("child-div");
body.onscroll = function(){
//console.log(documenhttps://fiddle.jshell.net/3urv0tp0/#tidyt.getElementById("child-div").style.top)
if(document.documentElement.scrollTop>=child.offsetTop)//Adjust Tolerance as you want
{
child.style.display="block"
}
};
I was looking for this either. Here i was trying to make "show text after scrolling to (number)px with fade effect". I wish it will work as it works for me :) The animation will be playing again if u scroll back to it, idk how to make it just one like in web u showed xd (i will edit if I find out)
window.addEventListener("scroll", function() {showFunction()});
function showFunction() {
if (document.body.scrollTop > 900 || document.documentElement.scrollTop > 900) {
document.getElementById("toptexts2").style.display = "block";
} else {
document.getElementById("toptexts2").style.display = "none";
}
}
.toptexts2 {
animation: fadeEffect 3s; /* fading effect takes 3s */
}
#keyframes fadeEffect { /* from 0 to full opacity */
from {opacity: 0;}
to {opacity: 1;}
}
<div class="toptexts2" id="toptexts2">
<div>Hi!</div>
<div>↓ go down ↓</div>
</div>
I like this:
var doc = document, dE = doc.documentElement, bod = doc.body;
function E(e){
return doc.getElementById(e);
}
function xy(e, d){
if(!d)d = 'Top';
d = 'offset'+d;
var r = e[d];
while(e.offsetParent){
e = e.offsetParent; r += e[d];
}
return r;
}
function x(e){
return xy(e, 'Left');
}
function y(e){
return xy(e);
}
var txt = E('theId'), txtS = txt.style;
onscroll = function(){
var left = dE.scrollLeft || bod.scrollLeft || 0;
var top = dE.scrollTop || bod.scrollTop || 0;
var w = innerWidth || dE.clientWidth || bod.clientWidth;
var h = innerHeight || dE.clientHeight || bod.clientHeight;
if(top > y(txt)-h){
txtS.display = 'none';
}
else{
txtS.display = 'block';
}
}
I left the left stuff in there, just in case, but you can probably remove it.
var div=$("#divtochange");
$(window).scroll(function () {
var windowpos = $(window).scrollTop();
//---check the console to acurately see what the positions you need---
console.log(windowpos);
//---------------------
//Enter the band you want the div to be displayed
if ((windowpos >= 0) && (windowpos <= 114)){
div.addClass("AfterScroll");
}
else{
div.removeClass("AfterScroll");
}

jQuery/JavaScript collision detection

How to detect if two <div> elements have collided?
The two divs are simple coloured boxes travelling perpendicular to each other, so no complicated shapes or angles.
var overlaps = (function () {
function getPositions( elem ) {
var pos, width, height;
pos = $( elem ).position();
width = $( elem ).width();
height = $( elem ).height();
return [ [ pos.left, pos.left + width ], [ pos.top, pos.top + height ] ];
}
function comparePositions( p1, p2 ) {
var r1, r2;
r1 = p1[0] < p2[0] ? p1 : p2;
r2 = p1[0] < p2[0] ? p2 : p1;
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function ( a, b ) {
var pos1 = getPositions( a ),
pos2 = getPositions( b );
return comparePositions( pos1[0], pos2[0] ) && comparePositions( pos1[1], pos2[1] );
};
})();
$(function () {
var area = $( '#area' )[0],
box = $( '#box0' )[0],
html;
html = $( area ).children().not( box ).map( function ( i ) {
return '<p>Red box + Box ' + ( i + 1 ) + ' = ' + overlaps( box, this ) + '</p>';
}).get().join( '' );
$( 'body' ).append( html );
});
body {
padding: 30px;
color: #444;
font-family: Arial, sans-serif;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
#area {
border: 2px solid gray;
width: 500px;
height: 400px;
position: relative;
}
#area > div {
background-color: rgba(122, 122, 122, 0.3);
position: absolute;
text-align: center;
font-size: 50px;
width: 60px;
height: 60px;
}
#box0 {
background-color: rgba(255, 0, 0, 0.5) !important;
top: 150px;
left: 150px;
}
#box1 {
top: 260px;
left: 50px;
}
#box2 {
top: 110px;
left: 160px;
}
#box3 {
top: 200px;
left: 200px;
}
#box4 {
top: 50px;
left: 400px;
}
p {
margin: 5px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<h1>Detect overlapping with JavaScript</h1>
<div id="area">
<div id="box0"></div>
<div id="box1">1</div>
<div id="box2">2</div>
<div id="box3">3</div>
<div id="box4">4</div>
</div>
General idea - you get the offset and dimension of the boxes and check whether they overlap.
If you want it to update, you can use setInterval:
function detectOverlapping() {
// code that detects if the box overlaps with a moving box
setInterval(detectOverlapping, 25);
}
detectOverlapping();
Also, note that you can optimize the function for your specific example.
you don't have to read the box dimensions repeatedly (like I do in my code) since they are fixed. You can read them on page load (into a variable) and then just read the variable
the horizontal position of the little box does not change (unless the user resizes the window). The vertical positions of the car boxes does not change. Therefore, those values also do not have to be read repeatedly, but can also be stored into variables.
you don't have to test whether the little box overlaps with all car boxes at all times. You can - based on its vertical position - figure out in which lane the box is currently, and test only the specific car box from that lane.
I believe this is the easiest way:
https://plugins.jquery.com/overlaps/
Here is another one, in German:
http://www.48design.de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11-8/
I'd give those a try.
--UPDATE--
I can't really spend anytime on it right now, but i can when i get home if no one answers but you;d do something like:
setInterval(function(){
//First step would be to get the offset of item 1 and item 2
//Second would be to get the width of each
//Third would be to check if the offset+width ever overlaps
//the offset+width of the 2nd
//Fourth would be, if so, do X or set a class...
},10);
Its a little late on this but I guess you could use this approach that I tried when I was faced with the similar situation. The advantage here is that there are no additional plugin, or scripts involved and neither do you have to introduce performance hungry polling into it.
This technique uses the the built-in methods and events that Jquery's droppable has to offer.
Ok, enough said, here's the solution technique:
Say if you have two elements (images in my case) and you don't want them to overlap or detect when they do, make the two elements a droppable and make them to 'accept' each other:
$([div1, div2]).droppable(CONFIG_COLLISSION_PREVENTION_DROPPABLE);
The 'CONFIG_COLLISSION_PREVENTION_DROPPABLE' looks like this:
var originatingOffset = null;
CONFIG_COLLISSION_PREVENTION_DROPPABLE = {
tolerance: "touch",
activate : function (event, ui) {
// note the initial position/offset when drag starts
// will be usedful in drop handler to check if the move
// occurred and in cae overlap occurred, restore the original positions.
originatingOffset = ui.offset;
},
drop : function (event, ui) {
// If this callback gets invoked, the overlap has occurred.
// Use this method to either generate a custom event etc.
// Here, i used it to nullify the move and resetting the dragged element's
// position back to it's original position/offset
// (which was captured in the 'activate' handler)
$(ui.draggable).animate({
top: originatingOffset.top + "px",
left: originatingOffset.left + "px"
}, 300);
}
}
The 'activate' and 'drop' handlers refer to the 'dropactivate' and 'drop' events of "droppable" plugin
Here, the key is the 'drop' callback. Whenever any of the two elements overlap and they are dropped over each other, the 'drop' will be called. This is the place to detect and take actions, may be sending out custom events or calling other actions (I here chose to revert the overlapping element's positions to the initial position when the drag started, which was captured in 'activate' callback).
That's it. No polling, no plugins, just the built-in events.
Well, there can be other optimizations/extensions done to it, this was simply the first shot out of my head that worked :)
You can also use the 'dropover' and 'dropout' events to signal and create a visual feedback to the user that two elements are overlapping, while they may be still on the move.
var CLASS_INVALID = "invalid";
// .invalid { border: 1px solid red; }
...
$.extend(CONFIG_COLLISSION_PREVENTION_DROPPABLE, {
over : function (event, ui) {
// When an element is over another, it gets detected here;
// while it may still be moved.
// the draggable element becomes 'invalid' and so apply the class here
$(ui.draggable).addClass(CLASS_INVALID);
},
out : function(event, ui) {
// the element has exited the overlapped droppable now
// So element is valid now and so remove the invalid class from it
$(ui.draggable).removeClass(CLASS_INVALID);
}
});
Hope this helps!
You can do this using getBoundingClientRect()
function isOverlapping(div1, div2){
const div1 = div1.getBoundingClientRect();
const div2 = div2.getBoundingClientRect();
return (div1.right > div2.left &&
div1.left < div2.right &&
div1.bottom > div2.top &&
div1.top < div2.bottom)
}
EDIT: I have written a blog post on my website. Here a link to it.
http://area36.nl/2014/12/creating-your-own-collision-detection-function-in-javascript/
Well I had the same problem but thanks to the answer of Oscar Godson I got a function that works. I used Jquery for easy coding and because i'm lazy ;p. I put the function in a other function that is fired every second so keep that in mind.
function collidesWith (element1, element2) {
var Element1 = {};
var Element2 = {};
Element1.top = $(element1).offset().top;
Element1.left = $(element1).offset().left;
Element1.right = Number($(element1).offset().left) + Number($(element1).width());
Element1.bottom = Number($(element1).offset().top) + Number($(element1).height());
Element2.top = $(element2).offset().top;
Element2.left = $(element2).offset().left;
Element2.right = Number($(element2).offset().left) + Number($(element2).width());
Element2.bottom = Number($(element2).offset().top) + Number($(element2).height());
if (Element1.right > Element2.left && Element1.left < Element2.right && Element1.top < Element2.bottom && Element1.bottom > Element2.top) {
// Do your stuff here
}
}
What it does is basically it gets all the values of element1 and then get all the values of element2. Then with the help of some calculations it figures out all the values. Then in the if statement it compares the square of element1 to the square of element2. If the values of element1 are between the left, right, top and bottom values of element2. If that is true the code in the bottom is executed.
I ran into this generalized issue myself, so (full disclosure) I made a plugin for it. For simple collision queries about static objects, try this:
http://sourceforge.net/projects/jquerycollision/
Which allows you to get a list of overlapping collision boxes (or none if there's no collision):
hits = $("#collider").collision(".obstacles");
Or to get a collision event during "dragging", use this:
http://sourceforge.net/apps/mediawiki/jquidragcollide/?source=navbar#collision
Which gives you a "collision" event to connect to. (Or a "protrusion" event, to see if a div escapes another div that currently contains it.)
$(draggable).bind(
"collision",
function(event,ui) {
...
}
);
If you are checking collisions during motion other than dragging, just call the original repeatedly, it's pretty quick. Note: the dragging one doesn't play nicely with resizing.
Post is old, May be it help someone...
function CheckDiv()
{
var ediv1 = document.getElementById('DIV1');
var ediv2 = document.getElementById('DIV2');
ediv1.top = $(ediv1).offset().top;
ediv1.left = $(ediv1).offset().left;
ediv1.right = Number($(ediv1).offset().left) + Number($(ediv1).width());
ediv1.bottom = Number($(ediv1).offset().top) + Number($(ediv1).height());
ediv2.top = $(ediv2).offset().top;
ediv2.left = $(ediv2).offset().left;
ediv2.right = Number($(ediv2).offset().left) + Number($(ediv2).width());
ediv2.bottom = Number($(ediv2).offset().top) + Number($(ediv2).height());
if (ediv1.right > ediv2.left && ediv1.left < ediv2.right && ediv1.top < ediv2.bottom && ediv1.bottom > ediv2.top)
{
alert("hi");
}
if (ediv1.left > ediv2.left && ediv1.top > ediv2.top && ediv1.right < ediv2.right && ediv1.bottom < ediv2.bottom)
{
alert("hello");
}
}

Categories

Resources