How to detect elements using "position: absolute"? - javascript

I'm trying to position an element exactly in the same "X" coordinate of another element.
There are many solutions in stackoverflow to get the position using something like this:
var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);
My problem is that when I try to apply the value on "element.style.left" of elements that are inside other elements positioned as "absolute", the element position becomes larger because the "left: 0" is the beginning of the absolute element and not "left: 0" of the document.
The more "nested" elements exist using "absolute", the greater the difference.
Is there any way to detect elements using "position: absolute" so that I can offset the actual value to be positioned by discounting the "left" of each element with "absolute"?
This is the problem:
<span class="dropdown-label">Some menu</span>
<nav class="dropdown-menu">
<a class="item">Email</a>
<a class="item">Twitter</a>
<a class="item">Tumblr Blog</a>
<span class="dropdown-label">Another menu</span>
<nav class="dropdown-menu">
<a class="item">Foo</a>
<a class="item">Bar</a>
<a class="item">I'm Batman</a>
<span class="dropdown-label">Yet another menu</span>
<nav id="finales" class="power-dropdown">
<a class="item">Foo</a>
<a class="item">Bar</a>
<a class="item">I'm Robin</a>
</nav>
</nav>
</nav>
Each dropdown needs to be positioned with the "lef" of its respective label, but we have dropdowns inside other dropdowns and each dropdown is positioned absolutely.
I know I can avoid the problem by using a "container" for each dropdown positioned as "relative" and so I do not need to dynamically set the "left" to anything.
The problem is that the HTML gets more verbose, which I'd like to avoid, plus I'd like to have control of how the dropdown appears to prevent it from being drawn off the screen.
But it seems like I've run into a problem that's impossible to solve, since I can not find anywhere a way to detect elements using "absolute" to be positioned.
Is there anything similar to "element.position" that gives me this information?

You asked for a way to detect position: absolute on elements.
Use window.getComputedStyle(element).position.
function findAbsoluteElementsFromList(list){
var ret = [];
for (var i = 0; i < list.length; i++)
if (window.getComputedStyle(list[i]).position === 'absolute')
ret.push(list[i]);
return ret;
}
console.log(findAbsoluteElementsFromList(document.querySelectorAll('div')));
.div {
width: 100px;
height: 100px;
position: absolute;
top: 0;
transform: translateY(50%);
}
#a {
position: relative;
left: 0;
background-color: #f00;
}
#b {
left: 25px;
background-color: #0f0;
}
#c {
left: 50px;
background-color: #00f;
}
<div class="div" id="a">
<div class="div" id="b">
<div class="div" id="c"></div>
</div>
</div>
Same function can be used to get all other CSS values.
What's the difference between Window.getComputedStyle and Element.getBoundingClientRect()? Read here.
Why can't you just use element.style.position? Read here.

Related

Having a html element stay in the same position without using position: fixed

To show what I want to do, here is a url. http://octopuscreative.com .
I want something that when I scroll down the height, the cyan div disappears like the website above.
I currently have the scrolling working in my code, however, I cannot see the rest of my HTML that is below my #main div. I don't know if this has anything to do with my new #slideshow div (with a fixed position).
I thought since the #slideshow div had its height reduced to 0, I would be able to see the HTML underneath the #main div, but all I see below is white.
var header = $('#slideshow'),
headerH = header.height();
$(window).scroll(function() {
if (header.height() >= 0) {
header.css({
height: -($(this).scrollTop() - headerH), position: 'absolute'
});
}
else if (header.height() < 0 ) {
header.css('height', 0);
header.css('position', 'absolute');
}
});
</script>
</head>
<body>
<div id="top">
<div id="stallion">
<img id="stallionpic" src="stallion1.png" />
<h1 class="h1">Stallion Stride</h1>
<div id="navigation">
<ul>
<li>Home</li>
<li>About</li>
<li>Register</li>
<li>Contact Us</li>
</ul>
</div>
</div>
</div>
<div id="main">
<div id="slideshow">
<div id="leftbutton"></div>
<div id="rightbutton"></div>
<div id="slideshownav">
<ul>
<li><a class="active"></a></li>
<li><a></a></li>
<li><a></a></li>
<li><a></a></li>
</ul>
</div>
<div id="slideshow_inner">
<li id="pic1"><a><img src="pic2.jpg" /></a></li>
<li id="pic2"><a><img src="pic1.jpg" /></a></li>
<li id="pic3"><a><img src="pic3.jpg" /></a></li>
<li id="pic4"><a><img src="pic4.jpg" /></a></li>
</div>
<p>a;lsdfja;lskdjf;laskdjf;aslkdjf;alsdjkfa;sldfkja;sldkfja;sldkfja;</p>
</div>
<div id="mainContent">
<p>a;lsdfja;lskdjf;laskdjf;aslkdjf;alsdjkfa;sldfkja;sldkfja;sldkfja;</p>
</div>
</div>
<div id="footer">
</div>
</body>
I'm the lead dev at Octopus. Here's the bare minimum amount of code to make something like that header effect work (it does use fixed position).
HTML
<div id="hero">
<h2>I am the hero</h2>
</div>
<div id="main-content">
<h3>I am the main content</h3>
</div>
CSS
* {
margin: 0;
}
div#hero {
position: fixed;
top: 0;
left: 0;
width: 100%;
background: #3D6AA2;
height: 300px;
}
div#main-content {
height: 1500px;
background: #fff;
margin-top: 300px;
position: relative;
z-index: 1;
}
I also threw together a jsFiddle that demonstrates it, along with the bit of parallax stuff the site does: http://jsfiddle.net/paulstraw/FW4FF/
Hope that helps!
position: fixed; positions the element based on viewable screen coordinates, so when you scroll, the position will update to reflect the 'new' top/left boundaries. position: absolute; is more like what you are describing, it will keep the element in the same place, regardless of the content around it, or the browser's scroll position. However, this will take the element out of the 'flow' of the page, and other elements will act like it is not there (and might overlap it). In which case, you will likely have more luck just floating your header left: float: left;.
If you do this, you will probably need to clear the floats in your main div by adding the css style: clear: both;. This will push the main div below any floated content above.

Get offset from element other than parent?

Using element.offset().left gets you the element's offset position from its parent. Is there any way to get the offset position from another element? For example, here's my html:
<div id="tile_id_579" class="product_tile">
<div class="selectContainer">
<table style="width:100%;">
<tbody>
<tr>
<td>
<div id="select-undefined" class="tzSelect">
<div id="options-undefined" class="tzOptions" style="max-height: 500px; width: 250px; display: none; min-width: 118px;">
<ul class="dropDown" id="dropdown-undefined">
<li><div class="header">Hand-Tossed Style Pizza</div>
<div class="subheader">The crowd-pleasing pizza that everyone can agree on.</div>
<div class="optkey">0</div>
</li>
<li><div class="header">Pan Pizza</div>
<div class="subheader">Our Pan Pizza is America's favorite!</div>
<div class="optkey">1</div>
</li>
</ul>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
It's generated from a database. Using offset().left gets me the offset from the #selecct-undefined div, but I need to get how far the #options-undefined div is from the .selectContainer div. Is this possible?
EDIT TO ADD
Tried both of these, and both return the same thing: top: 381, left: 0. But left is NOT 0 in relation to the document; it can't be.
var tip = $('#tile_id_579 #options-undefined .header');
tip.first().position();
and
var tip = $('#tile_id_579 #options-undefined .header');
tip.first().offset();
How is it possible that left is 0 for both when this is where the div is? Doesn't 0 left imply that it's all the way at the left of the browser window?
Since your element is indeed within that parent you specified you can use .position() instead of .offset(). But if .selectContainer is not the closest relative parent you will need to get the position of both your element and that "other" element and calculate the difference.
var myPos = $('#options-undefined').offset();
var otherPos = $('#options-undefined').closest('.selectContainer').offset();//if #options-undefined wasn't a child of .selectContainer you would do $('.selectContainer').offset();
var _offset = {
top: otherPos.top - myPos.top,
left: otherPos.left - myPos.left
}

How to increase the height of my dropdown?

I have a dropdown in HTML. When I hover on it, it expands. I added a new entry in the dropdown, and when I hover on it, I want my new entry visible completely (I added Comp3 in dropdown. I can see the upper part of it, but not completely Comp3). I tried various things like giving height to divs, increasing the height of the component in css, but nothing helped. Viewing the source code of that in the browser, this is the small code snippet of that particular dropdown:
<div class="optionsDropDown">
<p class="optionsDropDown collapseTrigger" id="userMenu">
Hello<em> User </em><span class="closed"></span>
</p>
<ul class="optionsDropDown collapseContent closed" name="userMenu">
<li>
<a class="optionsDropDown" href="javascript:showHelp();">
<span id="0">Comp1</span>
</a>
</li>
<li>
<a class="optionsDropDown" href="myAction.do?actionCode=3&page=controlPanel" target="view">
<span id="1">Comp3</span>
</a>
</li>
</ul>
</div>
Below is the javascript function that expands the dropdown:
$.fn.openMenu = function(menuContent){
$(menuContent).slideDown(200,function() {
$(menuContent).children().fadeTo('fast',1);
});
$('span', this).removeClass('closed');
};
And here is the dropdown class:
div.optionsDropDown {
float: right;
font-size: 11px;
height: 25px;
margin: 12px 32px 0 0;
position: absolute;
top: 0px;
right: 10px;
width: 120px;
z-index: 10000;
}
Please let me know if somehow height can be increased. Thanks in advance.
I believe your <div> and <ul> heights needs to be set to auto and the heights of each <li> needs to be adjusted.
Often times CSS becomes the practice of hit or miss when dealing with issues like these. First set everything to auto then systematically experiment with every permutation.
Good luck!

.position() doesn't point to the container

I have code like this:
<div id="item-menu">
<ul style="padding-top: 10px; padding-left: 20px;">
<li id="item-1">1</li>
<li id="item-2">2</li>
<li id="item-3">3</li>
</ul>
</div>
I want to get each item position relative to the container. In this example I will get each <li> position relative to the <ul>, right?
What should I do to get position of item-1 --> top: 10, left: 20?
Couldn't be simpler. Simply call the position() method on the lis:
var position = $("#item-?").position();
Returns an object with properties top and left. You'll also need to set the ul to position: relative in your CSS.
#item-menu { position: relative; }
fiddle: http://jsfiddle.net/ByZRV/2/
You can use .position() to compute the location of an element:
var offset = $('#item-1').position();
alert(offset.left + ', ' + offset.top); // 20, 10
Here's a demo.
You have to add the style position:relative for your
<ul style="position:relative; padding-top: 10px; padding-left: 20px;">
then
$('#item-menu ul li').each(function(){
console.info( $(this).position());
});

How do I pass javascript events from one element to another?

Setting up the stage:
I have 2 layers one on top of the other. The bottom layer contains links (simple images), The top layer contains advanced tooltip like hovers for the bottom layer. These tooltips can be large (they can overlap onto other links easily, and almost always overlap the link they are tooltipping).
My question:
I'd like my mouseover events to occur on the bottom layer, and then on mouseover display the tooltip in the upper layer. This way as you move off of the bottom link the tooltip in the upper layer goes away, and the tooltip for the new link can show up.
How do I take the events from the top layer and pass them to the layer below instead? So that the top layer is event transparent.
Example HTML:
jQuery(document).ready(function(){
jQuery('div.tile').click(function(){
jQuery('#log').html(this.id + " clicked");
return false;
});
jQuery('div#top').click(function(){
jQuery('#log').html('Top clicked');
return false;
});
});
.tile { width: 100px; height: 100px; position: absolute; }
.tile:hover, over:hover {border: 1px solid black;}
.over { width: 100px; height: 100px; position: absolute; display:none}
.stack, #sandwich { width: 400px; height: 400px; position: absolute; }
#tile1 {top: 50px; left: 50px;}
#tile2 {top: 75px; left: 10px;}
#tile3 {top: 150px; left: 310px;}
#tile4 {top: 250px; left: 250px;}
#tile5 {top: 150px; left: 150px;}
#over1 {top: 55px; left: 55px;}
#over2 {top: 80px; left: 15px;}
#over3 {top: 155px; left: 315px;}
#over4 {top: 255px; left: 255px;}
#over5 {top: 155px; left: 155px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<div id="sandwich">
<div class="stack" id="bottom">
<div class="tile" id="tile1">1</div>
<div class="tile" id="tile2">2</div>
<div class="tile" id="tile3">3</div>
<div class="tile" id="tile4">4</div>
<div class="tile" id="tile5">5</div>
</div>
<div class="stack" id="top">
<div class="over" id="over1">Tooltip for 1</div>
<div class="over" id="over2">Tooltip for 2</div>
<div class="over" id="over3">Tooltip for 3</div>
<div class="over" id="over4">Tooltip for 4</div>
<div class="over" id="over5">Tooltip for 5</div>
</div>
</div>
<div id="log"></div>
With the example javascript I've verified that the events work like normal, and only top is clicked. But I basically want the "over" items to be event transparent.
For people stumbling on this nine years later (like I did) the best way to do this now is with the CSS pointer-events property... simply set it to 'none' for your element(s) and behold the magic. No JS required.
https://caniuse.com/#search=pointer-events
Hope I understand the OP, but you can replicate the event on any selector after the original event has occurred: http://jsfiddle.net/Cr9Kt/1/
In the linked sample, I take the event on the top layer and create a similar event fired on the bottom layer. You can take this further with any individual clicking of each element(as opposed to top and bottom as a whole).
This is a borrowed idea from another question: Triggering a JavaScript click() event at specific coordinates
I am not quite certain that I understand what you are asking for. It sound to me a bit like a tool tip. Here is an example of how to do a tooltip this using jQuery, CSS and HTML.
http://jsbin.com/ilali3/13/edit
Hope that gets you started. If you add some more details, or modify that jsbin with more details we can iterate a bit.
I updated the example a bit to include storing tool tip information into the html element itself using jQuery. This is a bit cleaner.
Bob
This may seem overly complex and inelegant...it fakes your functionality.. but it works ;)
$(document).ready(function(){
var tileData = new Array();
// get coordinate data for each tile
$('div.tile').each(function(){
var tileInfo = {};
tileInfo.id = this.id;
tileInfo.text = $(this).text();
tileInfo.width = $(this).outerWidth();
tileInfo.height = $(this).outerHeight();
tileInfo.position = $(this).position();
tileInfo.coords = {};
tileInfo.coords.top = tileInfo.position.top;
tileInfo.coords.right = tileInfo.position.left + tileInfo.width;
tileInfo.coords.bottom = tileInfo.position.top + tileInfo.height;
tileInfo.coords.left = tileInfo.position.left;
tileData.push(tileInfo);
});
$('div.tile').click(function(){
$('#log').html(this.id + " clicked");
return false;
})
$('div#top').click(function(event){
$('#log').html('Top clicked');
// try to find tile under your mouse click
for(i=0; i<tileData.length;i++){
if(
event.pageX >= tileData[i].coords.left &&
event.pageX <= tileData[i].coords.right &&
event.pageY >= tileData[i].coords.top &&
event.pageY <= tileData[i].coords.bottom
) {
// found a tile! trigger its click event handler
$('#' + tileData[i].id).click();
}
}
return false;
});
});
Try it here: http://jsfiddle.net/neopreneur/vzq4z/1/

Categories

Resources