Scroll up and down to get Element into View with Selenium Python - javascript

I need to be able to scroll up and after that down to find some element with Selenium.
I already saw many questions and answers, the main idea I found is self.web_driver.execute_script("return arguments[0].scrollIntoView(true);", element) and this is what I currently have in my code. But this is not good enough since this code is scrolling down only so this fails to find the element in case it is located in the upper part of the rollable view.
So I need a script that first scrolls up (page Up?) and after that begins scrolling down.
I tried something like this
self.web_driver.execute_script("return arguments[0].scrollIntoView(true);", element)
self.web_driver.execute_script("window.scrollTo(0, -document.body.scrollHeight);")
self.web_driver.execute_script("return arguments[0].scrollIntoView(true);", element)
but this doesn't scroll up :(

You can try below code to scroll page up:
from selenium.webdriver.common.keys import Keys
self.web_driver.find_element_by_tag_name('body').send_keys(Keys.HOME)
Another way (preferred) you can scroll up to required element:
element = self.web_driver.find_element_by_xpath('SOME_XPATH') # you can use ANY way to locate element
coordinates = element.location_once_scrolled_into_view # returns dict of X, Y coordinates
self.web_driver.execute_script('window.scrollTo({}, {});'.format(coordinates['x'], coordinates['y']))

I have discovered a handy tricky hacky stuff over the years (I've been using Selenium for 150 years).
Whenever you're unable to scroll up a form, just send keys into an input on the top of the page. The driver will find it and will automagically scroll up the page to perform the action.
Whenever I think about this trick, I realize being an old man isn't that bad.
Good luck you fresh sailor, see you on the shores of Selenia.

Try this, it works good:
view_port_height = "var viewPortHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);"
element_top = "var elementTop = arguments[0].getBoundingClientRect().top;"
js_function = "window.scrollBy(0, elementTop-(viewPortHeight/2));"
scroll_into_middle = view_port_height + element_top + js_function
driver.execute_script(scroll_into_middle, element)

This was very useful for me:
WebElement scrolling = new WebDriverWait(driver, 10)
.until(ExpectedConditions.visibilityOfElementLocated(By.className("classSomething")));
//SCroll Down
((JavascriptExecutor) driver).executeScript("arguments[0].scrollTo(0, arguments[0].scrollHeight)", scrolling);
//Scroll Up
((JavascriptExecutor) driver).executeScript("arguments[0].scrollTo(0, arguments[0].scrollUp)", scrolling);

You can do it with also getBoundingClientRect() and scrollTo()
self.web_driver.execute_script("coordinates = arguments[0].getBoundingClientRect();scrollTo(coordinates.x,coordinates.y);", element)
I tested and it goes up and down.

If you defined driver as webdriver.Chrome() or webdriver.Firefox(),you can use this code for scrolling up:
from selenium.webdriver.common.keys import Keys
driver.find_element_by_tag_name('body').send_keys(Keys.HOME)
Also, for scrolling down, you can try this code:
driver.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight", scr1)
scr1 is your box xpath which you want to scroll it.
You can scroll to middle of the page by this code, too:
driver.execute_script("arguments[0].scrollTop = arguments[0].scrollWidth", scr1)

For java I use next expression:
WebElement element = drv.findElement(By.xpath("//div"));
(drv as JavascriptExecutor).executeScript("arguments[0].scrollIntoView();", element);
The snippet scroll down to an element in Selenium until it is visible

Related

Can a scrollIntoView function save its position?

I am having a problem with JS scrollIntoView getting reset after a selenium webElement.click() was performed. When I have used it on a custom scrollbox, it performs action correctly and then scrolls back to top after hitting .click().
element = driver.findElement(By.xpath("//div[#id='iLegenda']/ul/li[4]/i"));
((JavascriptExecutor)driver).executeScript("arguments[0].scrollIntoView(true);", element);
Thread.sleep(500);
driver.findElement(By.xpath("//div[#id='iLegenda']/ul/li[4]/i")).click(); //this line resets the position of the affected scrollbar
An alternate solution would be force-clicking the element even though it's not displayed on screen, but I haven't found any info on that front yet...
That's not something you can control. It's something to do with whatever the click is doing. If it were me, I would put the .scrollIntoView() code into a function so that it's easier to call, e.g.
public void scrollIntoView(WebElement e)
{
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", e);
}
and call it like
element = driver.findElement(By.xpath("//div[#id='iLegenda']/ul/li[4]/i"));
scrollIntoView(element);
Also, Thread.Sleep() is a bad practice. (You can google to learn the reasons why). You shouldn't need a wait here anyway but in case you do, it should be something like
WebDriverWait wait = new WebDriverWait(driver, 2);
WebElement element = driver.findElement(locator);
wait.until(ExpectedConditions.elementToBeClickable(element)).click();
I use Java and Selenium all the time and I've never had to scroll before clicking an element. Have you tried something simple like
driver.findElement(By.xpath("//div[#id='iLegenda']/ul/li[4]/i")).click();
with no scroll and no wait? It should just work.
Also, if you are going to click the same element repeatedly, you should do something like
By locator = By.xpath("//div[#id='iLegenda']/ul/li[4]/i");
driver.findElement(locator).click();
// do something
driver.findElement(locator).click();
// ... and so on...

Chrome ignoring hashes in URL

I've spent quite a while trying to find answers for this issue, but haven't had any success. Basically I need to scroll the user to the contact portion of the website when they go to healthdollars.com/#contact. This works just fine in Safari, but in Chrome I haven't had any luck. I've tried using jQuery/Javascript to force the browser to scroll down, but I haven't been able to.
Does anyone have any ideas? It's driving me crazy - especially since it's such a simple thing to do.
Not a full answer but in Chrome if you disable Javascript I believe you get the desired behavior. This makes me believe that something in your JavaScript is preventing default browser behavior.
It looks to me like the target element doesn't exist when when page first loads. I don't have any problem if I navigate to the page and then add the hash.
if (window.location.hash.length && $(location.hash)) {
window.scrollTo(0, $(location.hash).offset().top)
}
check for a hash, find the element's page offset, and scroll there (x, y).
edit: I noticed that, in fact, the page starts at #contact, then scrolls back to the top. I agree with the other answerer that there's something on your page that's scrolling you to the top. I'd search for that before adding a hack.
You can do this with JS, for example` if you have JQuery.
$(function(){
// get the selector to scroll (#contact)
var $to = $(window.location.hash);
// jquery animate
$('html'/* or body */).animate({ scrollTop: $to.offset().top });
});
The name attribute doesn't exists in HTML 5 so chrome looks to have made the name attribute obsolete when you use the DOCTYPE html.
The other browsers have yet to catch up.
Change
<a name="contact"></a>
to
<a id="contact"></a>
Maybe this workaround with vanilla javascript can be useful:
// Get the HTMLElement that you want to scroll to.
var element = document.querySelector('#contact');
// Stories the height of element in the page.
var elementHeight = element.scrollHeight;
// Get the HTMLElement that will fire the scroll on{event}.
var trigger = document.querySelector('[href="#contact"]');
trigger.addEventListener('click', function (event) {
// Hide the hash from URL.
event.preventDefault();
// Call the scrollTo(width, height) method of window, for example.
window.scrollTo(0, elementHeight);
})

Is protractor.findElement() meant to scroll to that element?

My current unit test (protractor + angularJS project) is failing with the error UnknownError: unknown error: Element is not clickable at point (525, 1103). I used debugger to stop it just before failure, and the only reason I can think that it would fail is because the button is not in the view port (you would have to scroll down).
The failing lines are
homeLink = ptor.findElement(protractor.By.linkText('Home'));
homeLink.click();
expect(ptor.getCurrentUrl()).toBe(homeUrl);
From https://github.com/angular/protractor/issues/319, he says '...when i use findElement() it will scroll them to the "top" of the page'. And the comments agree.
In my test homeLink = ptor.findElement(protractor.By.linkText('Home')); is not causing the the page to scroll.
Am I wrong in thinking it should?
What should I be doing?
You need to scroll down (or maximize browser if this allows you to see the button you would like to click on) first so that the button is visible on the page:
var scrollIntoView = function () {
arguments[0].scrollIntoView();
}
browser.executeScript(scrollIntoView, yourwebelement);
don't forget to get the webElement browser.driver.executeScript("arguments[0].scrollIntoView(true);", ed.getWebElement());

Javascript to determine beginning scroll position on page?

I have a site that features a "fixed" header, the problem is that it really messes up links that link further down the page a la "http://mysite.com/#lower_div_on_the_page"
Is it even possible to use javascript to do something like
if (URL has #hashtag) {starting scroll position = normal position + (my_header_height)}
Is this even possible?
EDIT:
Thanks for all the replies... really appreciated. For reference, I am DEFINITELY using jQuery... how would I do this with jQuery?
Yes, it's possible.
Here are the steps you need to take:
Set up a DOM Loaded event handler. There's more than one way to do this and here's a link to a web page that explains how to do this. Also if you're using a javascript framework such as jQuery (see .ready()) or Prototype.js (see observe extension) it would be a lot easier.
In the DOM loaded event handler function parse the URL (window.location) for the hashtag.
var hashTag = window.location.href;
hashTag = hashTag.substr(hashTag.indexOf('#') + 1);
// now hashTag contains the portion of the URL after the hash sign
Then if you recognize the anchor tag compute the desired scroll location based on that element's location in the DOM tree or whatever logic you would like to use. Again, jQuery (see .offset()) or Prototype.js (see cumulativeScrollOffset) should be able to help with determining the correct offset to scroll to.
Set the scroll of the page. Again jQuery (see .scrollTop()) or Prototype.js (see scrollTo) both have extensions to help with this.
Here's a jQuery example:
$(document).ready(function() {
var hashTag = window.location.href;
if(hashTag.indexOf('#') > 0)
{
hashTag = hashTag.substr(hashTag.indexOf('#'));
// now get the element's offset relative to the document
var offsetTop = $(hashTag).offset().top;
// and finally, scroll the document to that offset
$(document).scrollTop(offsetTop);
}
});
Sure is, you could do something as simple as:
if(window.location.hash != ''){
elementOffset = document.getElementById(window.location.hash.substr(1)).offsetTop;
window.scrollTo(0,elementOffset + my_header_height);
}
Using jQuery it'd obviously be simpler, and you would need to get extra offset depending on containing elements and such, but that should get you started.

goto HTML document location dynamically

My page adds # to the html programatically and have this in the tag
function InsertTag(){
//Add <a name="spot"></a> to the middle of this document
}
window.addEventListener('load', InsertTag, false);
my question is how can I make the document then jump to #spot?
Here's a suggestion: use id's instead. If you have:
<div id="something">
Then page.html#something will take you straight to that div. It doesn't have to be a div, it can be used on any element. If you can manipulate the DOM to add that anchor, I am pretty sure you'll be able to do this.
Now... To get there, you can use:
// this approach should work with anchors too
window.location.hash = 'something';
// or scroll silently to position
var node = document.getElementById('something');
window.scroll(0, node.offsetTop);
See it in action here: http://ablazex.com/demos/jump.html
There are subtle differences between the methods. Eg: The first one will cause the location on the address bar to be updated, the second one won't.
If you want it to look nicer you can use a jQuery plugin, like ScrollTo.
Try
window.location = currentUrl+'#spot';
where currentUrl is a variable having the address of the current url
You can try this.
var el = document.getElementById('spot');
var eloffsetTop = el.offsetTop;
window.scroll(0,eloffsetTop);

Categories