How do I get the absolute x,y coordinates for an HTML element on a page that may exist within a iFrame. The page is sometimes called on its own and sometimes is called from within an iFrame.
This is the function that I wrote to figure out the x,y position based on the offsetLeft and offsetTop of the element and the offseParent.
function getXY(obj)
{
var curObj = obj;
var curLeft = curTop = 0;
curLeft += curObj.offsetLeft
curTop += curObj.offsetTop;
while (curObj.offsetParent != null)
{
curObj = curObj.offsetParent;
curLeft += curObj.offsetLeft
curTop += curObj.offsetTop;
}
obj.x = curLeft;
obj.y = curTop;
}
This works great if the page is the top, but the problem is that if the page is run from within an iFrame I do not know the offset of the iFrame on the page.
Is there a way to get the exact, absolutes coordinates regardless of whether the page is in an iFrame or not?
Thank you.
I use JQuery Dimensions to do this. It does a good job of walking up the DOM and adding up all the offsets for you.
well, I would suggest the jquery dimensions suggested if that works for you. For getting to the parent iframe, you will need to lookup against window.parent. You could then get your offset recursively through parents.
if (window.parent && window.parent.frames && window.parent.document && window.parent.document.getElementsByTagName) {
var iframes = window.parent.document.getElementsByTagName("IFRAME");
for (var i = 0; i < iframes.length; i++) {
var id = iframes[i].id || iframes[i].name || i;
if (window.parent.frames[id] == window) {
//iframes[i] is your iframe in your parent.
}
}
}
I'm using this technique for identifying the frame in my FrameDialog plugin for jQueryUI.
Try using JavaScript framework like Dojo, or JQuery. They have all this basic functionality and work consistently across modern browsers.
In dojo:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.3/dojo/dojo.xd.js"></script>
// box is an oject {l, t, w, h, x, y}
// (x,y) are the absolute coordinates
// (l,t) are the left and top offsets relative to the parent container
box = dojo.coords(aDomObj);
see http://docs.dojocampus.org/dojo/coords for more details
Related
I am having issue fixing the header after scrolling, I tried a lot of stuff but can't get it to work. I checked this thread but it doesnt work for me: Angular 4 #HostListener Window scroll event strangely does not work in Firefox . This is my component structure:
Layout
Steps
Routes
Inside steps is my header which I want to fix, after scrolling for 50px. Inside Layout is some other content like a div with logo background (above the content of steps).
This is what I tried inside Steps.ts
#HostListener('window:scroll', [])
onWindowScroll() {
const number = window.scrollY;
if (number > 40) {
this.fixed = true;
} else if (this.fixed && number < 10) {
this.fixed = false;
}
}
but the problem is that scroll is not triggering at all. I found examples
where scroll logs the event, but for me it doesn't work (I tried with $event as well). Anyone has a solution?
Found a solution. On my layout component I put a function
(scroll)="onWindowScroll($event)"
and in layout component i used:
#HostListener('window:scroll', ['$event'])
onWindowScroll($event) {
const number = $event.target.scrollTop;
if (number > 40) {
this.fixed = true;
} else if (this.fixed && number < 10) {
this.fixed = false;
}
}
I removed Steps component since I didnt need it anymore, all the content is inside layout now.
In Angular 5+ it works a little differently:
const number = $event.target.scrollingElement.scrollTop || $event.target.documentElement.scrollTop;
Since some people come via Google to this question:
I'm quite a fan of moving logic like this into something re-useable. For Angular this would mean a directive. Therefore as I run into this issue myself I created a library from my code that at least has some tests and support across many browsers. So feel free to use this tested piece of code instead of polluting your components with more code.
https://w11k.github.io/angular-sticky-things/
With the code I see in the answer I did run into some issues. In another SO I found this solution. It is crucial to determine the offsetY of the header element correctly.
// Thanks to https://stanko.github.io/javascript-get-element-offset/
function getPosition(el) {
let top = 0;
let left = 0;
let element = el;
// Loop through the DOM tree
// and add it's parent's offset to get page offset
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return {
y: top,
x: left,
};
I'm having an issue with jQuery-UI draggables and droppables. I need to drag an draggable inside an droppable which is placed inside an iframe. This works ok until I scroll the iframe. The droppable coordinates are not updated.
The issue is demonstrated in this fiddle
I'm using the workaround below to make drag and dropping to iframes possible in the first place. It calculates the right offsets but does not use the iframe's scroll offsets. I tried but couldn't get it tweaked so it would take scroll offsets into account.
// Create new object to cache iframe offsets
$.ui.ddmanager.frameOffsets = {};
// Override the native `prepareOffsets` method. This is almost
// identical to the un-edited method, except for the last part!
$.ui.ddmanager.prepareOffsets = function (t, event) {
var i, j,
m = $.ui.ddmanager.droppables[t.options.scope] || [],
type = event ? event.type : null, // workaround for #2317
list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack(),
doc, frameOffset;
droppablesLoop: for (i = 0; i < m.length; i++) {
//No disabled and non-accepted
if (m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0], (t.currentItem || t.element)))) {
continue;
}
// Filter out elements in the current dragoged item
for (j = 0; j < list.length; j++) {
if (list[j] === m[i].element[0]) {
m[i].proportions().height = 0;
continue droppablesLoop;
}
}
m[i].visible = m[i].element.css("display") !== "none";
if (!m[i].visible) {
continue;
}
//Activate the droppable if used directly from draggables
if (type === "mousedown") {
m[i]._activate.call(m[i], event);
}
// Re-calculate offset
m[i].offset = m[i].element.offset();
// Re-calculate proportions (jQuery UI ~1.10 introduced a `proportions` cache method, so support both here!)
proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
typeof m[i].proportions === 'function' ? m[i].proportions(proportions) : (m[i].proportions = proportions);
/* ============ Here comes the fun bit! =============== */
// If the element is within an another document...
if ((doc = m[i].document[0]) !== document) {
// Determine in the frame offset using cached offset (if already calculated)
frameOffset = $.ui.ddmanager.frameOffsets[doc];
if (!frameOffset) {
// Calculate and cache the offset in our new `$.ui.ddmanager.frameOffsets` object
frameOffset = $.ui.ddmanager.frameOffsets[doc] = $(
// Different browsers store it on different properties (IE...)
(doc.defaultView || doc.parentWindow).frameElement
).offset();
}
// Add the frame offset to the calculated offset
m[i].offset.left += frameOffset.left;
m[i].offset.top += frameOffset.top;
}
}
}
Does anyone have an suggestion to fix the issue. Recommendations to achieve the same thing another way are also more than welcome.
You can change the proportions's height depending on the scroll amount in the iframe. The amount can be achieved using $("iframe").contents().scrollTop() as you have used it to change the scroll amount:
proportions = {
width: m[i].element[0].offsetWidth,
height: m[i].element[0].offsetHeight - $("iframe").contents().scrollTop()
};
Here's the DEMO.
I have an javascript, that find an element (panel), gets it position, and scrolls page to that position. The javascript function is called on button click. everything worsk fine, when the panel is Visible=true; But on the load of the page, the panel must be not visible, to the user.
When the panel is Visible = false, the javascript function doesn't works because the panel doesn't exists on the page.
when the panel is style="display:none" it exists, is not visible to the user, but it's position is not it's real position.
when the panel is style="visibility:hidden" the panel exists, is on the real position, but there is a big blank space on the page, where the panel is hidden.
How to make the panel invisible to the user without the blank space on the page, and get its real position on the page?
edit: my solution + code -
well, I made it working, but there must be better solution.
here's the code
<script type="text/javascript">
function elementPosition(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
curleft = obj.offsetLeft;
curtop = obj.offsetTop;
while (obj = obj.offsetParent) {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
}
}
return { x: curleft, y: curtop };
}
function ScrollToControl(id) {
jid = id;
var elem = document.getElementById(jid);
//here I set the display = 'block', and I also set this in button_click event
//when I didn't set it also in button_click event, I get the right panel position
//but the panel remains invisible
elem.style.display= 'block';
var scrollPos = elementPosition(elem).y;
window.scroll(0, scrollPos);
}
</script>
the button on the page:
<asp:Button ID="btnClientDossiers_EMAIL" runat="server" CssClass="button" Enabled="False"
Text="EMAIL" OnClick="btnClientDossiers_EMAIL_Click" />
codebehind
Page_Load
{
//how can I dynamically get the ctl00_ContentPlaceHolder?
// Or will the client name of plEMAIL always start with ctl00_ContentPlaceHolder?
btnClientDossiers_EMAIL.Attributes.Add("onclick", " return
ScrollToControl('ctl00_ContentPlaceHolder_plEMAIL');");
}
protected void btnClientDossiers_EMAIL_Click(object sender, EventArgs e)
{
plEMAIL.Attributes.Add("style", "display:block");
}
Please post your code so we can give you a more helpful answer, but I'm going suggest one way that you may be able to achieve what you are after. Try loading the page with the div(s) set to style="visibility:hidden;", and then get their vertical position into a global variable or a data-attribute of the element, and then immediately set the div's style to style="display:none;".
You should then be able to use these position values in your scroll function.
I have a javascript function (epoch calendar) which displays a calendar when focus is set on certain text boxes. this works fine in ie8, ff (all versions as far as I can test), opera etc but doesn't work in ie7 or previous.
If i have it set up in a blank html test page it will work so I'm fairly sure it's a conflict with my css (provided to me by a designer).
I've traced the error to these lines of code -
Epoch.prototype.getTop = function (element) //PRIVATE: returns the absolute Top value of element, in pixels
{
var oNode = element;
var iTop = 0;
while(oNode.tagName != 'BODY') {
iTop += oNode.offsetTop;
oNode = oNode.offsetParent;
}
return iTop;
};
Epoch.prototype.getLeft = function (element) //PRIVATE: returns the absolute Left value of element, in pixels
{
var oNode = element;
var iLeft = 0;
while(oNode.tagName != 'BODY') {
iLeft += oNode.offsetLeft;
oNode = oNode.offsetParent;
}
return iLeft;
};
More specifically, if i remove the actual while loops then the calendar will display OK, just that its positioning on the page is wrong?
EDIT
Code below which sets 'element'
<script type="text/javascript">
window.onload = function() {
var bas_cal, dp_cal, ms_cal;
dp_cal = new Epoch('epoch_popup', 'popup', document.getElementById('<%=txtDateOfDiag.ClientID%>'));
dp_cal = new Epoch('epoch_popup', 'popup', document.getElementById('<%=txtDOB.ClientID%>'));
};
</script>
Note: I am using asp.net Master pages which is why there is a need for the .ClientID
EDIT
A further update - I have recreated this without applying css (but including the .js file provided by the designer) the code still works fine which, there must be some sort of conflict between the CSS and my JavaScript?
That would lead me to believe that the tagName does not match, possibly because you have it in upper case. You might try while(!oNode.tagName.match(/body/i)) {
what happens if you add a line of debug code like this:
var oNode = element;
var iLeft = 0;
alert(oNode);
This might give different results in different browsers; I think it may be NULL for IE.
You may want to have a look at the code that provides the value of the 'element' parameter to see if there's a browser-dependant issue there.
i wonder if it is possible to create a bookmarklet to click on and the current webpage scrolls to the bottom!
javascript:function%20scrollme(){dh=document.body.scrollHeight;ch=document.body.clientHeight;if(dh>ch){moveme=dh-ch;window.scrollTo(0,moveme);}}
if i create a new bookmark and paste this as address nothing happens. I actually have no idea how to run javascript within a bookmarklet, however i just bookmarked the css-tricks Printliminator
maybe you could help, i would love to have a bookmarklet like this!
First, your JavaScript only defines a function and does nothing else.
Second, you need to use document.documentElement (which represents the <html> element) instead of document.body:
javascript:dh=document.documentElement.scrollHeight;ch=document.documentElement.clientHeight;if(dh>ch){moveme=dh-ch;window.scrollTo(0,moveme);}
or, simply
javascript:window.scrollTo(0,document.documentElement.scrollHeight)
(apparently it doesn't matter if y-coord of window.scrollTo is greater than the maximum position).
Update: In case you have to deal with IE in quirks mode, the root element is indeed document.body. Other browsers let document.documentElement.clientHeight represent the document's height (see Finding the size of the browser window, which deals with the window's height, but contains a nice table). Anyway, you want to set the position of the scroller to whatever is the greatest of the three:
javascript:window.scrollTo(0,Math.max(document.documentElement.scrollHeight,document.body.scrollHeight,document.documentElement.clientHeight))
Here is a function that smoothly scrolls down to the bottom of a page:
function scroll(scroll_to) {
if (scroll.timer) clearTimeout(scroll.timer);
var scroll_current = document.body.scrollTop,
distance = Math.abs(scroll_current - scroll_to);
if (scroll_current == scroll_to) return;
if (scroll_current > scroll_to) {
if (distance < 5) {
scroll_current -= distance;
} else {
scroll_current -= Math.ceil(distance / 10);
}
}
if (scroll_current < scroll_to) {
if (distance < 5) {
scroll_current += distance;
} else {
scroll_current += Math.ceil(distance / 10);
}
}
document.body.scrollTop = scroll_current;
scroll.timer = setTimeout(function() {
scroll(scroll_to)
}, 10);
}
If you call it:
scroll(document.body.scrollHeight - innerHeight);
it will scroll to the bottom of the page.
You can also use it to scroll to the top of the page like this:
scroll(0);
Just attach it to a button or link's onclick event.
you can simply use an anchor with this syntax
<a name="label">Any content</a>
and
Any content