JSFiddle: https://jsfiddle.net/1fLmtyoj/15/
I'm following this example to see if an element is overflowing, but something isn't working, this is for a DIV: Check with jquery if div has overflowing elements and
https://stackoverflow.com/a/7668692/1005607
I have an <LI> tag containing <SPAN>s. From the above answer, it's necessary to check (1) Overflow active, (2) Semi-visible parts. I combined them into the following function.
function isOverflowing(element) {
// 1. Main Part for overflow check
if (element.offsetHeight < element.scrollHeight ||
element.offsetWidth < element.scrollWidth) {
return true;
}
// 2. Partially invisible items
var invisibleItems = [];
for(var i=0; i<element.childElementCount; i++){
if (element.children[i].offsetTop + element.children[i].offsetHeight >
element.offsetTop + element.offsetHeight ||
element.children[i].offsetLeft + element.children[i].offsetWidth >
element.offsetLeft + element.offsetWidth ){
invisibleItems.push(element.children[i]);
}
}
if (invisibleItems.length) {
return true;
}
// Otherwise, neither Part 1 nor 2, return FALSE
return false;
}
In the JSFiddle, the expected output is that #1 and #3 are not overflowing, but #2 and #4 are overflowing. But all 4 are shown as not overflowing.
If you were to display element via the console, you would see that element does not have the attribute childElementCount -- but the first member of the element array does. Thus, refer to element[0] for all the attributes, and it behaves as expected. I think this is due to the selector you've used.
function isOverflowing(element) {
// 1. Main Part for overflow check
if (element.offsetHeight < element.scrollHeight ||
element.offsetWidth < element.scrollWidth) {
return true;
}
// 2. Partially invisible items
var invisibleItems = [];
for(var i=0; i<element[0].childElementCount; i++){
if (element[0].children[i].offsetTop + element[0].children[i].offsetHeight >
element[0].offsetTop + element[0].offsetHeight ||
element[0].children[i].offsetLeft + element[0].children[i].offsetWidth >
element[0].offsetLeft + element[0].offsetWidth ){
invisibleItems.push(element[0].children[i]);
}
}
if (invisibleItems.length) {
return true;
}
// Otherwise, neither Part 1 nor 2, return FALSE
return false;
}
$('#result').html('#1? ' + isOverflowing($('li:eq(0)')) +
'#2? ' + isOverflowing($('li:eq(1)')) +
'#3? ' + isOverflowing($('li:eq(2)')) +
'#4? ' + isOverflowing($('li:eq(3)'))
);
.type1 {
border: 1px solid black;
width: 130px;
height: 30px;
margin-bottom: 5px;
}
.type2 {
border: 1px dotted red;
width: 50px;
height: 25px;
margin-bottom: 45px;
}
.type3 {
border: 1px dashed blue;
width: 200px;
height: 40px;
margin-bottom: 5px;
}
.type4 {
border: 1px dashed green;
width: 100px;
height: 10px;
margin-bottom: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="type1"><span>Some text in LI 1</span></li>
<li class="type2"><span>Some more text in LI 2</span></li>
<li class="type3"><span>A long string of text in LI3</span></li>
<li class="type4"><span>A much longer string of text in LI4</span></li>
</ul>
<br/>
<br/>
<br/>
<br/>
<p id="result"></p>
As you say, however, this solution isn't generic enough -- it doesn't allow for nested DOM nodes. To fix this, we would want a recursive function. Try this, possibly. It's working, I think, but it really made my brain hurt to think recursively. ;)
function isOverflowing(element) {
/*******
* This is going to be a recursive function -- first,
* it'll check if there are children elements and,
* if not, simply return true. In the event there
* are child elements, it should recurse over each
* to see if any child overflows, whether partially
* invis or not.
******/
var elOverflows;
var el = element[0];
// On the first iteration, we initialize these. On every
// recursive iteration, we simply preserve them
var mainHeight = el.offsetTop + el.offsetHeight,
mainOffsetHeight = el.offsetHeight,
mainWidth = el.offsetLeft + el.offsetWidth,
mainOffsetWidth = el.offsetWidth;
// 1. Main Part for overflow check
if (mainOffsetHeight < el.scrollHeight ||
mainOffsetWidth < el.scrollWidth) {
elOverflows = true;
return elOverflows;
}
/***
* 2. If the current element doesn't contain any
* children, and the above check didn't return,
* then we don't have any overflow.
***/
if (el.childElementCount == 0) {
elOverflows = false;
return elOverflows;
} else {
// Here, we have child elements. We want to iterate
// over each of them and re-call isOverflowing() on
// each one. This is the recursion, allowing us to
// have any number of nested child elements.
$(el.children).each(function() {
elOverflows = isOverflowing($(this));
});
return elOverflows;
}
}
$("#container li").each(function() {
$("#result").append("<p>#" + $(this).index() + ": " + isOverflowing($(this)));
})
.type1 {
border: 1px solid black;
width: 130px;
height: 30px;
margin-bottom: 5px;
}
.type2 {
border: 1px dotted red;
width: 50px;
height: 25px;
margin-bottom: 45px;
}
.type3 {
border: 1px dashed blue;
width: 200px;
height: 40px;
margin-bottom: 5px;
}
.type4 {
border: 1px dashed green;
width: 100px;
height: 10px;
margin-bottom: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id='container'>
<li class="type1"><span>Some text in LI 1</span></li>
<li class="type2"><span>Some more text in LI 2</span></li>
<li class="type3"><span>A long string <span>containing A much longer string </span> in its center bit.</span>
</li>
<li class="type4"><span>A much longer string of text in LI4</span></li>
</ul>
<br/>
<br/>
<br/>
<br/>
<p id="result"></p>
I've made isOverflowing recursive, and for each child node of the current node, I simply re-call it again, until there are no more child nodes. passing the return value out to the initial call allows us to see if any of the child nodes exceed the bounds of the initial node.
Hope this helps!
Related
I have a drag and drop list of parent and child categories. I want to set the data attributes to the numerical order when I hit the test button. The parent data attribute currently works. But I'm having trouble getting the child data attribute to work correctly.
So for each parent, the child category should be number 1,2,3... and then the count should reset for the next parent. Right now the child doesn't reset for the new parent. How can I get the child to reference its parent so I can get the counter to reset?
Not sure if this is the best way to accomplish this. The end goal is to grab those data attributes and use that number to update the orderId in the database.
$(".categories").sortable({
connectWith: ".categories",
placeholder: "placeholder",
start: function(event, ui) {
if (ui.helper.hasClass('child-category')) {
ui.placeholder.removeClass('placeholder');
ui.placeholder.addClass('placeholder-sub');
} else {
ui.placeholder.removeClass('placeholder-sub');
ui.placeholder.addClass('placeholder');
}
},
sort: function(event, ui) {
var pos;
if (ui.helper.hasClass('child-category')) {
pos = ui.position.left + 20;
$('#cursor').text(ui.position.left + 20);
} else if (ui.helper.hasClass('parent-category')) {
pos = ui.position.left;
$('#cursor').text(ui.position.left);
}
if (pos >= 32 && !ui.helper.hasClass('child-category')) {
ui.placeholder.removeClass('placeholder');
ui.placeholder.addClass('placeholder-sub');
ui.helper.addClass('child-category');
ui.helper.removeClass('parent-category');
} else if (pos < 25 && ui.helper.hasClass('child-category')) {
ui.placeholder.removeClass('placeholder-sub');
ui.placeholder.addClass('placeholder');
ui.helper.removeClass('child-category');
ui.helper.addClass('parent-category');
}
}
});
$(".category").attr("data-order-parent", "");
$(".category").attr("data-order-child", "");
var i = 1;
$(".parent-category").each(function() {
$(this).attr("data-order-parent", i);
var j = 1;
$(".child-category").each(function() {
$(this).attr("data-order-child", j);
j++;
});
i++;
});
.categories {
list-style: none outside none;
}
.category {
background: #fff;
border: 1px solid #ddd;
cursor: move;
height: 50px;
line-height: 50px;
padding: 0 10px;
width: 480px;
}
.category:hover {
background-color: #F5F5F5;
}
.child-category {
margin-left: 40px;
width: 440px;
}
.placeholder {
background: #fff;
border: 1px dashed #ccc;
height: 50px;
width: 480px;
}
.placeholder-sub {
background: #fff;
border: 1px dashed #ccc;
height: 50px;
width: 440px;
margin-left: 40px;
}
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<button id="TEST" type="button">TEST</button>
<ul class='categories'>
<li class="category parent-category" data-order-parent="" data-order-child="">
categoryName-1
</li>
<li class="category parent-category" data-order-parent="" data-order-child="">
categoryName-2
</li>
<li class="category parent-category" data-order-parent="" data-order-child="">
categoryName-3
</li>
<li class="category parent-category" data-order-parent="" data-order-child="">
categoryName-4
</li>
</ul>
You should just iterate over your categories only once: children always follow after the last parent, so it is not so difficult to keep track.
Add this to the sortable object argument:
stop: function(event, ui) {
$(".category").attr("data-order-parent", "");
$(".category").attr("data-order-child", "");
var i = 1, j = 1;
$(".category").each(function() { // Iterate all categories (not only parents)
if ($(this).is(".parent-category")) { // Is it parent or child?
$(this).attr("data-order-parent", i++);
j = 1; // Reset child counter
} else {
$(this).attr("data-order-child", j++);
}
});
}
Update 10/4/18: I've updated the Snippet to reflected changes for anyone who may stumble upon this thread in seek of help. Existing check-boxes and newly added check-boxes will open/close the menu.
var statusChangeMenu, activeList, itemCheckBox, activeItems;
statusChangeMenu = document.getElementById("status-change-menu");
activeList = document.getElementById("active-items");
itemCheckBox = activeList.getElementsByClassName("item-checkbox");
activeItems = activeList.getElementsByClassName("active-item-text");
function addNewItem(event) {
event.preventDefault();
activeList.insertAdjacentHTML("afterbegin", "\
<li class=\"item\">\
<input class=\"item-checkbox\" type=\"checkbox\" name=\"checkbox\" />\
<span class=\"active-item-text\"></span>\
<button class=\"btn-complete\">complete</button>\
</li>");
activeItems[0].textContent = document.getElementById("new-item-text").value;
}
document.getElementById("btn-add-item").addEventListener("click", addNewItem, false);
activeList.addEventListener("change", function() {
var i, len;
for (i = 0, len = itemCheckBox.length; i < len || (i = 0); ++i) {
if (itemCheckBox[i].checked) {
i = 40;
break;
}
}
statusChangeMenu.style.height = i + "px";
}, false);
*{
margin: 0;
padding: 0;
}
body{
background-color: #393F4D;
}
header{
background-color: #1D1E22;
color: #FEDA6A;
text-align: center;
font-size: 10px;
}
main{
background-color: #707070;
max-width: 700px;
margin: auto;
margin-top: 20px;
padding: 15px;
}
#status-change-menu{
background-color: rgb(218, 123, 123);
margin-top: 10px;
height: 0px;
overflow: hidden;
transition: all .25s ease-in-out;
}
#status-change-menu>button>img{
height: 40px;
}
form{
background-color: #D4D4DC;
padding: 10px;
text-align: right;
box-shadow: 1px 1px 3px;
}
#new-item-text{
width: 100%;
}
#btn-add-item{
padding: 5px;
box-shadow: 1px 1px 3px;
}
.item-list-container{
background-color: #D4D4DC;
margin-top: 20px;
box-shadow: 1px 1px 3px;
}
.item{
background-color: rgb(165, 233, 222);
list-style: none;
display: grid;
grid-template-columns: auto 1fr max-content;
grid-template-rows: 30px;
margin-top: 10px;
}
.item-checkbox{
grid-column: 1/2;
width: 30px;
margin:auto;
}
.active-item-text{
grid-column: 2/3;
background: rgb(252, 252, 252);
overflow: hidden;
}
.btn-complete{
grid-column: 3/4;
}
.item>input{
height: 20px;
}
<body id="the-list">
<header>
<h1>The List V4</h1>
</header>
<main>
<form action="#">
<textarea name="textbox" id="new-item-text" cols="30" rows="1"></textarea>
<button type="submit" id="btn-add-item">Add</button>
</form>
<div id="status-change-menu" class="change-menu">
<h3>Status Change Menu</h3>
<button class="btn-bar-hold">BTN1<img src="img/btn_hold.svg" alt=""></button>
<button class="btn-bar-delete">BTN2<img src="img/btn_delete.svg" alt=""></button>
</div>
<div class="item-list-container">
<ul id="active-items" class="item-list">
<li class="item">
<input class="item-checkbox" type="checkbox" name="checkbox">
<span class="active-item-text">random text text random</span>
<button class="btn-complete">complete</button>
</li>
<li class="item">
<input class="item-checkbox" type="checkbox" name="checkbox">
<span class="active-item-text">random text text random</span>
<button class="btn-complete">complete</button>
</li>
</ul>
</div>
</main>
</body>
I'm working on a simple checklist web app using pure vanilla HTML, CSS, javascript. I've been stuck in one part all weekend. Hoping someone can shed some light on what I'm missing or doing wrong. Here's where I'm at.
My Goal
Whenever an item in the checklist (ul) is selected (via checkbox), a hidden menu slides out with various options to manipulate the selected item(s). The menu must stay visible if any of the checkboxes on the list are checked. The menu must close if no checkboxes are checked.
Where I'm Stuck
I'm able to get the menu to slide out during a 'change' event of the checkbox, but I can't get the menu element to react after the initial change event. During debugging, it also appears the menu element is not reacting to the checkbox is in a 'checked' state, but simply just reacting to the checkbox being changed in general. Here's the JS code I have, but I've tested various other configurations with no success.
Code Pen with Full Code & Snippet of related JS code below.
Updated Codepen 10/4/18
https://codepen.io/skazx/pen/mzeoEO?
var itemCheckBox = document.querySelectorAll('input[type="checkbox"]')
var statusChangeMenu = document.getElementById("status-change-menu")
for(var i = 0 ; i < itemCheckBox.length; i++){
itemCheckBox[i].addEventListener("change", function(){
if (!itemCheckBox.checked)
{statusChangeMenu.style.height = "40px";}
else
{statusChangeMenu.style.height = "0px";}
})}
I've read a few dozen different post and articles, but most were related to only having 1 checkbox or used jquery. Let me know if you need any further details. Thank you!
itemCheckBox refers to a NodeList returned by querySelectorAll, not an individual element, so saying itemCheckBox.checked doesn't really make sense.
You should be checking if any checkbox in the list is checked, which you can use with the .some() function, like so:
Here's a working demo
for (var i = 0; i < itemCheckBox.length; i++) {
itemCheckBox[i].addEventListener("change", function(event) {
if (!event.target.checked) {
statusChangeMenu.style.height = "40px";
} else {
statusChangeMenu.style.height = "0px";
}
});
}
var itemCheckBox = document.querySelectorAll('input[type="checkbox"]');
var statusChangeMenu = document.getElementById("status-change-menu");
function changeHandler (event) {
// get the list of checkboxes in real time in case any were added to the DOM
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
var anyChecked = [].some.call(checkboxes, function(checkbox) { return checkbox.checked; });
// alternatively (instead of using .some()):
// var anyChecked = false;
// checkboxes.forEach(function (checkbox) {
// if (checkbox.checked) {
// anyChecked = true;
// }
// });
if (anyChecked) {
statusChangeMenu.style.height = "40px";
} else {
statusChangeMenu.style.height = "0px";
}
}
for (var i = 0; i < itemCheckBox.length; i++) {
itemCheckBox[i].addEventListener("change", changeHandler);
}
for (var i = itemCheckBox.length; i < itemCheckBox.length + 2; i++) {
// add some checkboxes dynamically
var newCheckbox = document.createElement("input");
var newLabel = document.createElement("label");
newLabel.innerText = "Checkbox " + (i + 1);
newCheckbox.type = "checkbox";
// -- IMPORTANT-- bind event listener on dynamically added checkbox
newCheckbox.addEventListener("change", changeHandler);
newLabel.appendChild(newCheckbox);
newLabel.appendChild(document.createElement("br"));
document.body.appendChild(newLabel);
}
#status-change-menu {
height: 0;
background-color: red;
overflow: hidden;
color: white;
font-weight: bold;
}
<div id="status-change-menu">I should be visible if any checkboxes are checked</div>
<label>Checkbox 1<input type="checkbox"/></label><br/>
<label>Checkbox 2<input type="checkbox"/></label><br/>
<label>Checkbox 3<input type="checkbox"/></label><br/>
mhodges is correct in that itemCheckBox is a NodeList, not an individual element. Another issue is that you are trying to test if the box that changed is checked, and if it isn't, you are closing the menu. As you described, that is not what you want.
You need another way to check to see if all check boxes are unchecked before you close the menu. A simple way to do that is just another inner loop in the onChange function:
for(var i = 0 ; i < itemCheckBox.length; i++){
itemCheckBox[i].addEventListener("change", function(){
showMenu = false
for(var j = 0; j < itemCheckBox.length; j++)
{
if(itemCheckBox[j].checked)
showMenu = true
}
if (showMenu)
{statusChangeMenu.style.height = "40px";}
else
{statusChangeMenu.style.height = "0px";}
})}
Heres a modified Snippet
I'm looking to learn how to do this left menu :
http://js.devexpress.com/New/15_2/#HTML_5_JS_Core
When you scroll down the page, the "active" menu item change.
p.s.
Is there a name for this type of menu?
regards,
yaniv
Scroll Navigation
That is how we call these type of navigation bars. Basically you have to listen to the scroll event and calculate which element is in the viewport at the moment than you add a class to your navigation that marks the current menu element.
There is a nice demo built in jQuery but because jQuery is a thing of the past, I built one in Vanilla JS. See comments for explanations.
There are different ways to define which is the current element. In my Example it is the last one whose top line just passed the top line of the browser.
Working demo
window.onscroll = onScroll;
function onScroll() {
var removeActiveClass = function (elements) {
for (var i = 0; i < elements.length; ++i) {
elements[i].classList.remove('active');
}
}
var anchors = document.querySelectorAll('#menu-center a');
var previousRefElement = null;
for (var i = 0; i < anchors.length; ++i) {
// Get the current element by the id from the anchor's href.
var currentRefElement = document.getElementById(anchors[i].getAttribute('href').substring(1));
var currentRefElementTop = currentRefElement.getBoundingClientRect().top;
// Searching for the element whose top haven't left the top of the browser.
if (currentRefElementTop <= 0) {
//The browser's top line haven't reached the current element, so the previous element is the one we currently look at.
previousRefElement = anchors[i];
// Edge case for last element.
if (i == anchors.length - 1) {
removeActiveClass(anchors);
anchors[i].classList.add("active");
}
} else {
removeActiveClass(anchors);
previousRefElement.classList.add("active");
break;
}
}
}
body, html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
.menu {
width: 100%;
height: 75px;
position: fixed;
background-color:rgba(4, 180, 49, 0.6);
}
#menu-center {
width: 980px;
height: 75px;
margin: 0 auto;
}
#menu-center ul {
margin: 15px 0 0 0;
}
#menu-center ul li {
list-style: none;
margin: 0 30px 0 0;
display: inline;
}
.active {
font-size: 14px;
color: #fff;
text-decoration: none;
line-height: 50px;
}
a {
font-size: 14px;
color: black;
text-decoration: none;
line-height: 50px;
}
.content {
height: 100%;
width: 100%;
}
#portfolio {background-color: grey;}
#about {background-color: blue;}
#contact {background-color: red;}
<div class="menu">
<div id="menu-center">
<ul>
<li><a class="active" href="#home">Home</a></li>
<li>Portfolio</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
</div>
<div id="home" class="content"></div>
<div id="portfolio" class="content"></div>
<div id="about" class="content"></div>
<div id="contact" class="content"></div>
This is not exactly menu type, it is the way how you can position objects by html.
You can use position:Abosule property to achieve this effect:
http://www.w3schools.com/css/tryit.asp?filename=trycss_position_fixed
By this given divs are "flying" above the res of the page. In your case it could be a menu.
EDIT:
To sync this you need to detect when given anchor is currently seen.
It can be done by jQuery, this is sample draft of code, should explain clue of solution:
// list of header on page
var positions = [
$("#anchor1").offset().top,
$("#anchor2").offset().top,
$("#anchor3").offset().top,
];
var menu_objects= [
"#menu1",
"#menu2",
"#menu3"
];
var $w = $(window).scroll(function(){
// clear old
for(var v in menu_objects)
$(v).css({"color","white"});
for(var i=positions.length-1;i>=0;i--)
{
if(positions[i]>=$w.scrollTop())
{
$(menu_objects[i]).css({"color","red"});
break;
}
}
});
I want to use JavaScript to check if one div element (that can be dragged) is touching another div element.
Here is some code:
<div id="draggable" style="position: absolute; top: 100px; left: 200px; background-color: red; width: 100px; height: 100px;"></div>
<div style="background-color: green; width: 100px; height: 100px;"></div>
Can this be done?
If so, how?
Edit: I do not want to use jQuery, just plain old JavaScript!
A plain old JS solution
Below is a "plain old JavaScript" rewrite of the overlap detection function found in this answer to the question titled "jQuery/Javascript collision detection".
The only real difference between the two is the replacement of the use of jQuery to get element position and width for calculating the bounding box.
Native JavaScript makes this task easy via the Element.getBoundingClientRect() method, which returns the four values needed to create the position matrix returned by the getPositions function.
I added a click handler for the boxes as a simple demonstration of how you might use the function to compare a target (clicked, dragged, etc.) element to a set of selected elements.
var boxes = document.querySelectorAll('.box');
boxes.forEach(function (el) {
if (el.addEventListener) {
el.addEventListener('click', clickHandler);
} else {
el.attachEvent('onclick', clickHandler);
}
})
var detectOverlap = (function () {
function getPositions(elem) {
var pos = elem.getBoundingClientRect();
return [[pos.left, pos.right], [pos.top, pos.bottom]];
}
function comparePositions(p1, p2) {
var r1, r2;
if (p1[0] < p2[0]) {
r1 = p1;
r2 = p2;
} else {
r1 = p2;
r2 = 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 clickHandler(e) {
var elem = e.target,
elems = document.querySelectorAll('.box'),
elemList = Array.prototype.slice.call(elems),
within = elemList.indexOf(elem),
touching = [];
if (within !== -1) {
elemList.splice(within, 1);
}
for (var i = 0; i < elemList.length; i++) {
if (detectOverlap(elem, elemList[i])) {
touching.push(elemList[i].id);
}
}
if (touching.length) {
console.log(elem.id + ' touches ' + touching.join(' and ') + '.');
alert(elem.id + ' touches ' + touching.join(' and ') + '.');
} else {
console.log(elem.id + ' touches nothing.');
alert(elem.id + ' touches nothing.');
}
}
#box1 {
background-color: LightSeaGreen;
}
#box2 {
top: 25px;
left: -25px;
background-color: SandyBrown;
}
#box3 {
left: -50px;
background-color: SkyBlue;
}
#box4 {
background-color: SlateGray;
}
.box {
position: relative;
display: inline-block;
width: 100px;
height: 100px;
color: White;
font: bold 72px sans-serif;
line-height: 100px;
text-align: center;
cursor: pointer;
}
.box:hover {
color: Black;
}
<p>Click a box to see which other boxes are detected as touching it.<br />
<em>If no alert dialog box appears, open your console to view messages.</em></p>
<div class="box" id="box1">1</div>
<div class="box" id="box2">2</div>
<div class="box" id="box3">3</div>
<div class="box" id="box4">4</div>
Update: I realize now that this only accounts for the intersection of the top left corner of the target element and therefore, doesn't provide a complete solution. But I'll leave it up for posterity in case someone finds it useful for other purposes.
Use element.getBoundingClientRect() and document.elementFromPoint()
You can use element.getClientBoundingRect() (src) to get the position of the target (clicked, dragged, etc.) element.
Temporarily hide the target element, then use document.elementFromPoint(x, y) (src) to get the top-most element at that position, and then check it's class name for comparison (you could compare any attribute or property instead, if you prefer).
To achieve cross-browser compatible behavior from this method
read: document.elementFromPoint – a jQuery solution (You don't
have to use jQuery to achieve this result. The method can be
replicated in pure JS.)
Addendum:
I am only showing the function for detecting overlap instead of showing drag-and-drop or drag-to-move functionality because it isn't clear which of those, if either, you are trying to implement and there are other answers showing how to accomplish various drag patterns.
In any case, you can use the detectCollision() function below in combination with any drag solution.
var box2 = document.getElementById('box2'),
box3 = document.getElementById('box3');
box2.onclick = detectCollision;
box3.onclick = detectCollision;
function detectCollision(e) {
var elem = e.target,
elemOffset = elem.getBoundingClientRect(),
elemDisplay = elem.style.display;
// Temporarily hide element
elem.style.display = 'none';
// Check for top-most element at position
var topElem = document.elementFromPoint(elemOffset.left, elemOffset.top);
// Reset element's initial display value.
elem.style.display = elemDisplay;
// If a top-most element is another box
if (topElem.className.match(/box/)) {
alert(elem.id + " is touching " + topElem.id);
} else {
alert(elem.id + " isn't touching another box.");
};
}
#box1 {
background-color: LightSeaGreen;
}
#box2 {
top: 25px;
left: -25px;
background-color: SandyBrown;
}
#box3 {
background-color: SkyBlue;
}
.box {
position: relative;
display: inline-block;
width: 100px;
height: 100px;
}
.clickable {
cursor: pointer;
}
<div class="box" id="box1"></div>
<div class="box clickable" id="box2"></div>
<div class="box clickable" id="box3"></div>
I am trying to, sort of, emulate the effect here. Essentially, during scrolling, change the css (drop shadow), and when the element comes back to original position (remove shadow).
I am able to detect scroll, but not able to figure out how to detect the return to the original un-scrolled state.
HTML
<div id="container">
<ul>
<li id="one">el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li>
</ul>
</div>
CSS
html, body {
margin: 0;
padding: 0;
height: 100%;
}
#container {
height: 100px;
width: 500px;
border: 1px solid #000000;
overflow: scroll;
}
JS (with jquery)
var p = $('#one');
var position0 = p.position().top;
$('#container').scroll(function () {
if (p.position().top != position0) {
console.log('p.position: ' + p.position().top);
$('#container').css('background-color', 'pink');
}
});
JSFIDDLE: http://jsfiddle.net/nrao89m3/
PS: From console.log it doesn't seem to return to its original value at all.
Just add an else block:
var p = $('#one');
var position0 = p.position().top;
$('#container').scroll(function () {
if (p.position().top != position0) {
console.log('p.position: ' + p.position().top);
$('#container').css('background-color', 'pink');
} else {
$('#container').css('background-color', 'white');
}
});
http://jsfiddle.net/vyjbwne2/