I'm looking for a generic solution to find the scrollable container of an element (I'd like to listen to the scroll event).
The normal approach would be to traverse up in the DOM tree in check the parent elements until one of them is scrollable - here are some solutions like this.
However this will fail if shadow DOM is also in the picture.
Here's an example structure:
<app-home>
<ion-content>
#shadow-root
<div class="inner-scroll">
<slot>
-> <my-element> (reveal)
</slot>
</div>
<my-element>...</my-element>
</ion-content>
</app-home>
In the above example the scrollable element is .inner-scroll, which is inside the shadow dom of ion-content. Since my-element gets into the container through the slot, traversing up the tree will never reach the scroll container (my-element -> ion-content -> app-home).
I found that ion-content has a getScrollElement method, which can be used in this specific case, however I'd like to know if there's a generic DOM based solution, since I'd like this to work regardless of the context.
UPDATE:
To be more clear, I'd like my-element to be a standalone, reusable component, which can find it's scrolling container, no matter where it's placed.
What I found, that the built in scrollIntoView method works on the DOM element, and it scrolls the correct container, meaning that the browser somehow figures it out, but does not expose a method which returns the scroll container.
Another idea was to listen to the scroll event on a higher level, without knowing the exact element, but this is not working either, because the scroll event does not propagate.
Any ideas?
Get the <ion-content> element and use its shadowRoot element to query the scrollable element.
const ionContent = document.querySelector('app-home > ion-content')
const scrollable = ionContent.shadowRoot.querySelector('.inner-scroll')
Then you can use querySelector in your scrollable element to find the desired element. If there are more scrollable elements in the shadow DOM, you can use querySelectorAll to return an array of nodes.
It may be too late, but answering if it helps anyone else looking for an answer.
One possible solution is to use the getComputedStyle w.r.t the parent element and keep iterating upwards until you find a minimum of 2 combinations - height+width is longer than its own parent + it has overflow property (via getComputedStyle) set to anything that allows scroll.
Related
Right now I have a web page with:
A fixed header at the top of varying size, covering up the page's main content.
A page with a deeply nested scroll container.
Within that container, several other deeply nested elements.
What I want to do is scroll that container so that one of the elements inside lines up with the top of the visible portion of the container. I believe what I am looking for is the top of the nested element relative to the top of the visible portion of the container.
Unfortunately I cannot use .offsetTop because that only references an element's immediate parent, and due to the structure of the page these elements are too deeply nested for that to be of any use. It's always a fixed number. Same with the scroll container; its immediate parent is not the window. Frustratingly, the header is not of a fixed height, so I can't hardcode it into my calculations very easily. I also cannot use jQuery.
I tried using getBoundingClientRect for both the element and the container, but they are always a fixed distance away from each other and it doesn't give me the information I need about how far the element is scrolled relative to the container's visible area, unfortunately.
EDIT: We have a scrolling solution in place already, but we were hoping to improve it through a certain proposed means that I am trying to determine the feasibility of. I would like to ask that everyone please try to only answer the question in bold, rather than proposing alternate solutions. I really do appreciate any help on offer, but I am hoping to get some extremely specific information based on an extremely specific scenario.
I also had this kind of issue earlier, you can try this to auto scroll to the element
var elem = <some select operation that returns the element>;
elem.id = <some temp id> //if no id is there
var a = document.createElement('a');
a.href = "#" + elem.id;
a.click();
If you wish, you can remove the id from your element once its done.
I am using pure Polymer/Javascript and need to scroll to the bottom of my main panel. Since it is a scrollable element within a fixed-size container the typical JS answer
window.scrollTo(0,document.body.scrollHeight);
Does not work.
Couldn't find a direct solution so posting an the answer myself. Hope this helps someone :)
(Based on what I found here)
//Get the main paper-drawer-panel element
a = document.querySelector("paper-drawer-panel [main]")
//use the undocumented scroller property and set it to the scroller's height
a.scroller.scrollTop = a.scroller.scrollHeight
UPDATE:
I also discovered that if you select any element or container within the panel there should be scrolling methods attached to them allowing you to to scroll to the top or bottom of the panel based on the selected element.
//Get the main paper-drawer-panel element
a = document.querySelector("some-element-container-in-paper-panel");
// Passing in false scrolls to the bottom of the container, no param to the top.
a.scrollIntoView(false)
I may not comment (I need 50 points of something) but scrollIntoView() is experimental technology. Not supported by Chrome.
I'm creating some HTML elements dynamically via JavaScript. Something line this:
var author = document.createElement("div");
author.innerHTML = _feed[i].author;
author.className = "author";
The browser returns offsetHeight = 0 if the element is not added to the document yet.
I want to know the element's height before appending them to the document because if the element's resulting height is too big I need to take some actions (make it smaller), and only after that add it to the document.
My suggestion is to add it to the page with a left margin of -10000 or something, so that it's not visible. Then you can get the dimensions, do what you need, and then change the left margin so that it's where you want it. This is assuming you would use absolute positioning.
But so far as I know, the browser will not report that information on elements not in the DOM.
Dan the problem you are going to have is that unless you add it to the DOM then you can never accurately know what the height will be. The reason for this is that as the element is not in the DOM, it cannot be affected by styles set in CSS or the browsers default styles. Even if the element itself has no specific styles applied to it then its parent element may do, or even its grandparent element (did I just make grandparent element up?).
The only accurate way of getting the height is to place the element exactly where it will sit in the DOM and then detect the height. I don't think visibility:hidden; will work as its presence will still be felt by elements around it (pushing margins around and positioning of other elements).
How about position:absolute; left: -5000px;? That may work.
I'm not quite sure how to describe what I'm looking to do, but I'll do my best.
At the moment I have a parent <div>, with absolutely positioned child <div>s within it, and I'm tracking the mouse pointer location coordinates relative to the element your mouse is over.
At the moment, when I mouse over my child <div>s, I get the mouse location relative to them, but I want the coordinates to be relative the the parent <div> even when mousing over the child elements.
So I basically want the child elements to be visible, but transparent to the mousemove, so I want the mousemove to go straight through to the parent element.
How would I do this? Do I maybe need to somehow make the parent <div> be in the foreground but still have the child <div>s show through? or make a transparent <div> overlay just to get the mouse coordinates?
You can make an element "transparent" to events by setting its pointer-events CSS property to none. For example:
<div><!--container-->
<div style="pointer-events: none"><!--inner div; will not respond to mouse clicks-->
</div>
</div>
Okay, I've worked out a way I can ignore the child elements when I mouse over them.
When getting the target of the event I can just change the target to the parentNode if the className of the target matches something:
if (target.className.toLowerCase() === 'ignoreme') {
target = target.parentNode;
}
If you put your event handlers on the parent div, then it'll be what gets the events as they bubble up. As to the positioning issue, it might make your life easier to make your parent div be position: relative. The mouse coordinates in the event are always relative to the window, as far as I know, so you're going to have to do the math anyway. I'm really dumb and I've been able to figure that out in the past through trial and error :)
you're using event.target (srcElement) in your code, just get rid of that and replace with the div in question.
Consider:
$("#PlotPlace").append('<div style="position:absolute;left:200px;top:40px;font-size:smaller">Hello world!</div>');
I need to execute that line only if the width of the resultant text would be less than 60px. How can I check the width before placing the object?
Unfortunately, the div will only have a width value once it is rendered into the DOM.
I would append that content to an inconspicuous area of the document, perhaps even absolutely positioned so that no flow disruption occurs, and make sure that it is set to "visibility:hidden". That way it will be inserted into the DOM and be rendered, but be invisible to the viewer.
You can then check the width on it, and move it into position and set it to "visibility:visible" at that point. Otherwise, you can remove it from the document.
Maybe you can append it invisible, then check it's width, and then consider to show or hide.
$("#PlotPlace").append('<div style="position:absolute;left:9001px;top:40px;font-size:smaller">Hello world!</div>');
var div = $('#PlotPlace').children("div");
if(div.width() < 60)
div.css({left:200})
Sounds like something you'd have to hack. I don't believe the JavaScript runtime in any browser has an event you can hook into in between calculating the layout and displaying the element, so you can add it in a way that it can't be seen and doesn't affect the height (doesn't cause additional scrolling), and then show/hide it based on the width at this point. It's hacky and ugly, but because you don't have many event hooks it might be the only way to do it.
You can´t. At least not so easy. The text you insert is written in a specific font, which must be rendered by the browser, then you know the width of the element. By the Way, what exactly do you want to insert, with such a restriction? Wouldn´t it be simpler to cut the text within the output parameters?