Div absolute position relative to static elements - javascript

I have a div element which I'm using as a pop-over search field which I want to have appear under the element which is being filtered. However, it seems that I cannot use the style.bottom and style.left of the element I want the field to be relative to as this element is static.
Example is here: http://www.is-epic.co.uk/example/example.html
Clicking the Header 2 link will have the input box appear, in the top-left corner of the table. I would like it to appear roughly where Data 1.2 is. How do I achieve this?
(Code in example.html is on one page, in live dev CSS and JS are in separate files)

Set the element you wish to position the other element with respect to to position: relative.
This will make it the containing block for any descendants that are position: absolute (unless an element between the two is also position: not static).

this works in FF and Google-Chrome
var head = document.getElementById("header_2");
var filter = document.getElementById("search_filter");
filter.style.display = "";
filter.style.left = head.offsetLeft + 'px';
filter.style.top = head.offsetTop + head.offsetHeight + 'px';
it should work with IE as well..
i used variables filter and head to cut down on typing :)

The problem is that for header_2 both style.left and style.bottom are 0, so that
document.getElementById("search_filter").style.left =
document.getElementById("header_2").style.left;
document.getElementById("search_filter").style.top =
document.getElementById("header_2").style.bottom;
is equivalent to
document.getElementById("search_filter").style.left = 0;
document.getElementById("search_filter").style.top = 0;
which is exactly what happens. You have to find out header_2's actual position, e.g. using jQuery.

Related

How do you add an annotation to a website without altering the layout?

I'm working on a hobby project similar to markup.io where you can load in any website and annotate it. I can't figure out how to add an annotation that behaves like it does in markup.io:
Doesn't interrupt the styling or layout of the website you are annotating
The annotation keeps the correct position when scrolling and resizing the window
From what I can see they place an absolute positioned div inside the element that you clicked on. From my understanding by reading the docs that div would position itself based on the closest positioned ancestor. How would you calculate the correct top and left values to position the annotation to where the user clicked? Is there a better way to do this?
I'm using React if that matters.
Things that I have tried:
Append the following bit of html to the element that was clicked:
<div style="width:0px; height:0px; position:relative;">
<div style="width:50px;height:50px;position:absolute; ">this is the annotation </div>
</div>
Problem: This would mess with the page layout because of the relative positioned div that is not ignored by the document flow.
Create fixed overlay over the entire page. Get the css selector of the clicked element. Draw annotation on the fixed overlay at the x,y position of the element.
Problem: Whenever the user would scroll or resize the window the annotation would need to be redrawn at the new position of the element. I used getBoundintClientRect to get the new position and this would cause a reflow and caused the whole website to have severe perfomance issues when dealing with 100+ annotations.
Hopefully someone can point me in the right direction!
The general idea is as follows:
Find the parent of the element that you clicked on
Check if they are positioned (anything other than static)
If it is static search for the closest element that is positioned.
Set the new badge/annotation top and left position to that of the mouse minus the top and left of the element that you're going to append it to (in this case called parent).
Also account for the width and height by subtracting half of each to perfectly center your annotation.
// In my case I put the webpage in an Iframe. If this is your own page
// you can just use document.
iframe.contentWindow.document.addEventListener(
'click',
(e: MouseEvent) => {
// step 1: find the parent.
let parent = e.target.parentElement;
let computedStyle = window.getComputedStyle(parent);
// step 2 & 3: Look up the first positioned element and make this the
// the element that you're going to append your badge/annotation to.
while (
computedStyle.position === 'static' &&
parent.parentElement !== null
) {
parent = parent.parentElement;
computedStyle = window.getComputedStyle(parent);
}
// style the annotation the way you want to
const badge = document.createElement('div');
const { top, left } = parent.getBoundingClientRect();
badge.style.position = 'absolute';
// step 4 and 5 get the mouse position through e.clientX and Y and
// subtract the appropriate value like below to place it exactly at the mouse position
badge.style.top = `${e.clientY - top - 5}px`;
badge.style.left = `${e.clientX - left - 5}px`;
badge.style.backgroundColor = 'red';
badge.style.width = '10px';
badge.style.height = '10px';
badge.style.borderRadius = '50%';
badge.style.zIndex = '9999';
parent.appendChild(badge);
}
);

Javascript/jQuery get true width and position of float affected element?

Is it possible to get the width (using javascript or jQuery) of a float-affected element? When text is being pushed over due to a floating image is it possible to get its position and true width? I have attached an image to explain better.
Code example,
<div>
<img style="...float: left"/>
<h1>A title!</h1>
<p>Text!</p>
<h1>New header added.</h1>
</div>
Picture
I need to find the width starting from the arrow, (the gray box is the image)(the dotted line is the width according to Firefox inspect mode).
I would like to avoid changing all the elements display types if possible.
Thank you!
I'm a little late to the party, but I had a similar problem and came up with a solution which (so far) seems to work in all instances of this issue. I like this solution because as far as I can tell, it works independent of the floating element - all you need is the element whose true width/position you want to get, nothing more. I've done it in pure Javascript for speed purposes, but it can easily be streamlined with jQuery and a separate CSS Stylesheet if you so choose.
//Get the rendered bounding box for the content of any HTMLElement "el"
var getLimits = function(el) {
//Set a universal style for both tester spans; use "!important" to make sure other styles don't mess things up!
var testerStyle = 'width: 0px!important; overflow: hidden!important; color: transparent!important;';
//Create a 'tester' span and place it BEFORE the content
var testerStart = document.createElement('SPAN');
testerStart.innerHTML = '|';
var testerFloat = ' float: left!important;';
testerStart.setAttribute('style', testerStyle + testerFloat);
//Insert testerStart before the first child of our element
if (el.firstChild) {
el.insertBefore(testerStart, el.firstChild);
} else {
el.appendChild(testerStart);
}
//Create a 'tester' span and place it AFTER the content
var testerEnd = document.createElement('SPAN');
testerEnd.innerHTML = '|';
testerFloat = ' float: right!important;';
testerEnd.setAttribute('style', testerStyle + testerFloat);
el.appendChild(testerEnd);
//Measure the testers
var limits = {
top: testerStart.offsetTop,
bottom: testerEnd.offsetTop + testerEnd.offsetHeight,
left: testerStart.offsetLeft,
right: testerEnd.offsetLeft
}
//Remove the testers and return
el.removeChild(testerStart);
el.removeChild(testerEnd);
return limits;
};
So, in your case, the code would just be:
var paragraphBoundingBox = getLimits($('div>p').get(0));
A couple things to note:
1) The float direction would be reversed if you are using an RTL language
2) All of the four edge positions in the output object are relative to the el.offsetParent - use this handy function can find their positions relative to the document.
First of all, the "full width" is exactly the true width.
You can watch this picture, it can help you understand why the true width and true position of the affected element is the way firefox tells you.
http://i.stack.imgur.com/mB5Ds.png
To get the width of inline text where it's pushed right by the float image, there's no good way except using the full width minus the float image's width.
var w = $('p').width()
- $('img').width()
- $('img').css('margin-left').replace("px", "")
- $('img').css('margin-right').replace("px", "")
- $('img').css('padding-left').replace("px", "")
- $('img').css('padding-right').replace("px", "")
- $('img').css('border-left-width').replace("px", "")
- $('img').css('border-right-width').replace("px", "");

Insert a logo next to any DOM element

I'm developing a Firefox extension which amends the contents of a loaded webpage. First I select all the elements of which the "src" or "href" attributes match my regex (this part of the code works).
Then, I would like to place a little image at the top right corner of the found element's parent using the following code:
/* create and add attributes to image */
var img = window.content.document.createElement("img");
var b = window.content.document.createAttribute("src");
b.nodeValue = "chrome://plugin/content/agent.png";
img.setAttributeNode(b);
img.addEventListener("click", function(){ alert("ds"); });
img.style.display = "block";
img.style.border = "3px solid red";
img.style.position = "relative";
img.style.top = "-10px";
img.style.right = "-10px";
img.style.left = "20px";
// ... the code to return the element...
//now insert the image
$jq(img).appendTo(element.parentNode);
The current result is that either the image is shown just at the bottom of the element's parent or not shown at all.
If you look at this: http://jsfiddle.net/yzwh5/64/ - I would like my button to work in a similar manner to that red cross.
You must "play" with the element's CSS positioning, in fact it doesn't matter where do you insert the images, but where you do position them.
Maybe you would like to take a look at "next-to", a jQuery plugin that automates the calculations to position an element next to another element
For example:
<script type="text/javascript">
$('.PlaceThisDiv').nextTo($('.ThisOtherDiv'), {position:'right', shareBorder:'top'});
</script>
As you can see in this Fiddle i have prepared (contains the plugin itself)
http://jsfiddle.net/PvcNr/
you will get you something like this:
More info: https://code.google.com/p/next-to/
Hope it helps
Try CSS code like this:
.my-ext-overlay:after {
content:url(smiley.gif);
position: absolute;
margin-left: -16px; margin-top: -16px;
}
and then adding the ".my-ext-overlay" class name to each element you find.
See example
Firstly, CSS floats are called cssFloat (or htmlFloat in some browsers) because float is a reserved word. Second, there is no such float value as block.
Third, you missed an x in -10px for the right property.
Fourth, setting both relative left and right positions can lead to unexpected behaviour.
Fifth, you shouldn't use createAttribute, since attribute nodes aren't reliable in all browsers. Instead, use setAttribute on the element.
Sixth, if this did work it would mess up page layout around the element you're searching for, so you would be better off with position: absolute so it doesn't affect the flow. If you do this, however, you should use margin-left instead of left (same for other directions), to shift the element around.
I think that should at least get the thing close to working...

Absolutely position div sits behind textbox in IE7

I'm having issues with an absolutely positioned custom drop down, in IE7 the div is sitting behind a normal textbox which is not position in any special way.
I've already read this topic: IE7 puts absolutely positioned div underneath, ignores z-index but the solutions didn't help me.
The difference between my code and the person who asked the above question is that my div is being created through javascript using document.write and adding it through appendChild
addEvent(document.getElementById("storeDown"), "click", showRegions);
var showRegions = function(e) {
var dd = getTarget(e);
if(document.getElementById("regionOptions")) {
dd.parentNode.removeChild(document.getElementById("regionOptions"));
regionsShowing = false;
} else {
var ddNode = document.createElement("div");
ddNode.id = "regionOptions";
ddNode.style.backgroundColor = offColor;
var optNode;
for(var region in storeList) {
optNode = document.createElement("div");
optNode.innerHTML = region;
ddNode.appendChild(optNode);
}
dd.parentNode.appendChild(ddNode);
regionsShowing = true;
}
cancelEvent(e);
}
My parent div has a z-index of 500 and is positioned relative and regionOptions is positioned absolute and I tried to also add a z-index to it but it didn't make a difference.
Okay finally figured it out, I changed where it appends so it's appended in a div at the bottom of the page after all the textboxes (in my case, a buttons div), so instead of dd.parentNode.appendChild(ddNode), it's now document.getElementbyId("buttons").appendChild(ddNode), but also had to change the CSS so it's absolute positioned in relative to the page wrapper rather than the specific div, and it seems to have fixed it.

Find the "potential" width of a hidden element

I'm currently extending the lavalamp plugin to work on dropdown menus but I've encountered a small problem. I need to know the offsetWidth of an element that is hidden. Now clearly this question makes no sense, rather what I'm looking for is the offsetWidth of the element were it not hidden.
Is the solution to show it, grab the width, then hide again? There must be a better way...
The width of an element that has CSS visibility: hidden is measurable. It's only when it's display: none that it's not rendered at all. So if it's certain the elements are going to be absolutely-positioned (so they don't cause a layout change when displayed), simply use css('visibility', 'hidden') to hide your element instead of hide() and you should be OK measuring the width.
Otherwise, yes, show-measure-hide does work.
The only thing I can think of is to show it (or a clone of it) to allow retrieval of the offsetWidth.
For this measurement step, just make its position absolute and its x or y value a big negative, so it will render but not be visible to the user.
You can use the following function to get the outer width of an element that is inside a hidden container.
$.fn.getHiddenOffsetWidth = function () {
// save a reference to a cloned element that can be measured
var $hiddenElement = $(this).clone().appendTo('body');
// calculate the width of the clone
var width = $hiddenElement.outerWidth();
// remove the clone from the DOM
$hiddenElement.remove();
return width;
};
You can change .outerWidth() to .offsetWidth() for your situation.
The function first clones the element, copying it to a place where it will be visible. It then retrieves the offset width and finally removes the clone. The following snippet illustrates a situation where this function would be perfect:
<style>
.container-inner {
display: none;
}
.measure-me {
width: 120px;
}
</style>
<div class="container-outer">
<div class="container-inner">
<div class="measure-me"></div>
</div>
</div>
Please be aware that if there is CSS applied to the element that changes the width of the element that won't be applied if it's a direct descendant of body, then this method won't work. So something like this will mean that the function doesn't work:
.container-outer .measure-me {
width: 100px;
}
You'll either need to:
change the specificity of the CSS selector ie. .measure-me { width: 100px; }
change the appendTo() to add the clone to a place where your CSS will also be applied to the clone. Ensure that where ever you do put it, that the element will be visible: .appendTo('.container-outer')
Again, this function assumes that the element is only hidden because it's inside a hidden container. If the element itself is display:none, you can simply add some code to make the clone visible before you retrieve it's offset width. Something like this:
$.fn.getHiddenOffsetWidth = function () {
var hiddenElement $(this)
width = 0;
// make the element measurable
hiddenElement.show();
// calculate the width of the element
width = hiddenElement.outerWidth();
// hide the element again
hiddenElement.hide();
return width;
}
This would work in a situation like this:
<style>
.measure-me {
display: none;
width: 120px;
}
</style>
<div class="container">
<div class="measure-me"></div>
</div>
Two options:
position the element outside the viewport (ex: left:-10000px)
use visibility: hidden or opacity: 0 instead of hide().
Either way will work as hiding the element but still being able to get the computed width. Be careful with Safari on thi, it's awfully fast and sometimes too fast...
Actual jQuery plugin!
Usage:
console.log('width without actual: ' + $('#hidden').width());
console.log('width with actual: ' + $('#hidden').actual('width'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.actual/1.0.19/jquery.actual.min.js"></script>
<div style="width: 100px; display: none;">
<div id="hidden"></div>
</div>
If you know the element to be the full width of a parent element another approach is to create a recursive method:
es5:
var getWidth;
getWidth = function($el){
return $el.offsetWidth || getWidth($el.parentElement);
}
var width = getWidth(document.getElementById('the-element'));
es6:
let getWidth
getWidth = ($el) => $el.offsetWidth || getWidth($el.parentElement)
const width = getWidth(document.getElementById('the-element'))
What I did was ;
by the time hiding that element, stored its width in its dataset.
It only will work for you if you can hide programmatically.
ie.
When Hiding ;
var elem = $("selectorOfElement");
elem.dataset.orgWidth = elem.clientWidth;
Later when getting ;
var elem = $("selectorOfElement");
var originalWidthWas = elem.dataset.orgWidth;
thats because its hidden via display: none; What ive done in the past is to make a "reciever" div which i use absolute positioning on to get it off the page. Then i load the new element into that, grab the dimensions and then remove it when im done - then remove the reciever when im done.
Another thing you can do is to not use hide(); but to instead set visibility: hidden; display: ; However this means the blank area will be rendered wherever the node is attached.
var $hiddenElement = $('#id_of_your_item').clone().css({ left: -10000, top: -10000, position: 'absolute', display: 'inline', visibility: 'visible' }).appendTo('body');
var width = parseInt($hiddenElement.outerWidth());
$hiddenElement.remove();
I try to find working function for hidden element but I realize that CSS is much complex than everyone think. There are a lot of new layout techniques in CSS3 that might not work for all previous answers like flexible box, grid, column or even element inside complex parent element.
flexibox example
I think the only sustainable & simple solution is real-time rendering. At that time, browser should give you that correct element size.
Sadly, JavaScript does not provide any direct event to notify when element is showed or hidden. However, I create some function based on DOM Attribute Modified API that will execute callback function when visibility of element is changed.
$('[selector]').onVisibleChanged(function(e, isVisible)
{
var realWidth = $('[selector]').width();
var realHeight = $('[selector]').height();
// render or adjust something
});
For more information, Please visit at my project GitHub.
https://github.com/Soul-Master/visible.event.js
demo: http://jsbin.com/ETiGIre/7
Sorry I am late to this conversation. I am surprised no one has mentioned getComputedStyle. (Note this only works if the CSS sets a width value)
Grab the element:
let yourEle = document.getElementById('this-ele-id');
and use the function:
getComputedStyle(yourEle).width
This returns a string so you will have to remove the numbers from the string.
This works even when the element's display style is set to none.
Other articles to read about this includes here at zellwk.com

Categories

Resources