ashot icon indicating copy to clipboard operation
ashot copied to clipboard

Remove fixed nav duplication from screeshot

Open khabali opened this issue 7 years ago • 21 comments

When a website site have a fixed navigation menu. It's appear on all the segment

of the screenshot. Try a screenshot of http://stackoverflow.com screenshot

It's will be nice if we can avoid that

khabali avatar May 22 '17 07:05 khabali

Hi, @khabali. Thank you for interesting issue. Do you have any ideas how to fix it? I have only one idea - to remove or hide this fixed bar while the screenshot is being taken. Actually it is known issue for many years but there is no common way to avoid this problem. Any idea will be appreciated.

pazone avatar May 24 '17 15:05 pazone

we may let the user to give a div identifier that will be ignored after the first screenshot. the user will be responsible of finding this identifier and passing it to the API.

WebElement navBar= webDriver.findElement(By.cssSelector("#my_fixed_element"));
new AShot()
  .withFixedElementOnTop(navBar) // this will be ignored while taking the screenshot
  .takeScreenshot(webDriver);

khabali avatar May 26 '17 09:05 khabali

Here is a workaround for that to avoid the noisy duplication of the fixed element This don't solve the problem completely but at least the screenshot is more clean even if we loose the fixed element.

        // Remove the fixed element from the page
        JavascriptExecutor js;
        if (driver instanceof JavascriptExecutor) {
            js = (JavascriptExecutor) driver;
            WebElement element = driver.findElement(By.className("element-class"));
            js.executeScript("arguments[0].parentNode.removeChild(arguments[0])", element);
        }
        //take the screenshot
        new AShot().takeScreenshot(webDriver);

As a solution we can take two screenshots, one without the fixed element as shown above and another of only the fixed element then we can merge the two of them in one shcreenshot as a final clean result https://stackoverflow.com/questions/2318020/merging-two-images

khabali avatar May 26 '17 11:05 khabali

any update on this issue? i don't like the idea of editing the page before taking the shot. i have tried taking only the content webelement like this:

WebElement content = driver.findElement(By.xpath("//div[@class='content-page']"));
		BufferedImage image = new AShot().shootingStrategy(ShootingStrategies.viewportPasting(100))
				.takeScreenshot(driver, content).getImage();

it only works for the first shot, when it scrolled down, it still took the top bar in the shot

image

marvelfrozen avatar Jul 20 '17 03:07 marvelfrozen

@gavrilfb I'm totally agree with you. It is one of not resolved problems of our world. Any ideas and proposal will be highly appreciated.

pazone avatar Jul 25 '17 13:07 pazone

I forgot to mention it in the last post, but as you can see in the screenshot, some rows sometimes missing. in the example above is row 5.

I don't know if it's possible to copy the function, but I used ShareX before for manual scrolling screen capture. It's in C# though. Below is the result after it process the image. image

marvelfrozen avatar Jul 26 '17 03:07 marvelfrozen

I forgot that we have non-documented feature which is the workaround for headers. Mostly it is used for iOS screenshots. It allows to cut off predefined area from top or bottom of origin screenshot. Use ShootingStrategy.viewportNonRetina(int scrollTimeout, int headerToCut, int footerToCut). For example ShootingStrategy.viewportNonRetina(0, 100, 0) will cut 100 px off from top of each screenshot.

pazone avatar Aug 30 '17 12:08 pazone

From what I have tried, to achieve 'beautiful' shot of long pages, you have two options:

  • Use PhantomJS driver and maximize the windows
  • Use JQuery to find max height of all elements on page and set the window heigth to that value

Of course the second method won't work on pages with infinite scrolling. Hope this help.

marvelfrozen avatar Nov 01 '17 04:11 marvelfrozen

@gavrilfb ShootingStrategy.viewportNonRetina(int scrollTimeout, int headerToCut, int footerToCut) implements second approach.

pazone avatar Nov 01 '17 14:11 pazone

It seems that the implementation leads to black bars at the places where the headers and footers would otherwise be. My expectation was that a single seamless picture would emerge. Isn't that possible or am I possibly doing something wrong?

I also have another suggestion: Should it not be possible to follow the algorithm that is used at CrossBrowserTesting and apparently leads to a perfect result completely automatically, without having to make any static height specifications? See here for details: https://help.crossbrowsertesting.com/screenshots/general/screenshots-fixed-elements/

drundanibel avatar Jul 10 '18 15:07 drundanibel

@drundanibel the example provided in your link is getbootstrap.com/2.3.2/ I checked the page and indeed it is possible to stop the navbar follow scrolling by editing the css attribute. But this implementation won't work on my case, since my navbar is not using position: fixed. Nevertheless, this could be one way to solve this scenario. Would be great if you can share or even commit the codes to do this. Of course we need to restore the actual value before changes after taking the screenshot too for consistency.

marvelfrozen avatar Jul 11 '18 04:07 marvelfrozen

@gavrilfb: It seems to me that good solution approaches have already been found in this thread: https://stackoverflow.com/questions/19474320/detect-whether-an-element-has-positionfixed-possibly-by-parent-element-via-jq

Edit: Just for the sake of interest: How does your navbar work, if not with "position: fixed"?

Can you or anyone else please say something about my other question with the black bars? I have applied the viewportNonRetina() workaround tip mentioned above by pazone to the page http://getbootstrap.com/2.3.2/:

Screenshot screenshot = new AShot().shootingStrategy(ShootingStrategies.viewportNonRetina(100, 40, 0)).takeScreenshot(driver)

I got this full page screenshot with two 40 pixel high horizontal black bars at the places where otherwise on both scrolled pages the footer would have been:

fullpagescreenshot

Apart from the ugly black bars as such, the content of the page covered by them does not appear in the following image section either.

What am I doing wrong?

drundanibel avatar Jul 11 '18 13:07 drundanibel

Hello guys. I was experiencing this problem since yesterday and I found a workarround for it. The problem is with the header menu bar, ok? So, you can change the attributes of an element with Selenium (using JavascriptExecutor). My solution was change the visibility of header menu to hide it and make the screenshots. After that, I turned back the attribute to the original one (just for keep the behavior along the tests). With that change I can peform a full screenshot perfectly. Without black bars or overlapped menu.

Example:

WebElement element = driver.findElement(By.cssSelector("selector")); ((JavascriptExecutor) driver).executeScript("arguments[0].style.visibility='hidden'", element); Screenshot screenshot = new AShot().shootingStrategy(ShootingStrategies.viewportPasting(100)) .takeScreenshot(driver); ((JavascriptExecutor) driver).executeScript("arguments[0].style.visibility=''", element);

Lufis avatar Jul 24 '18 16:07 Lufis

@Lufis: Thanks for sharing your workaround, which I will try out later. But what I would wish for as a perfect solution would be if the header could be seen on the upper edge and the footer on the lower edge of the full page screen shot - with no header or footer or black bars between, of course. :) If I understood your approach correctly, header and footer are not visible at all, are they?

drundanibel avatar Jul 25 '18 08:07 drundanibel

Is there any progress in this matter? Can I still hope that ashot will be able to handle fixed elements appropriately in the future?

By the way, I came across this Chrome browser extension in the meantime, which creates full page screenshots exactly as I would expect them to: https://chrome.google.com/webstore/detail/full-page-screen-capture/fdpohaocaechififmbbbbbknoalclacl

See here in contrast to the ashot result in my post above:

screencapture-getbootstrap-2-3-2-2018-10-29-12_24_22

In my Selenium test suites I will now temporarily switch to this somewhat unclean solution in the hope that ashot will take up this challenge in the future. I would be very happy, many thanks in advance!

drundanibel avatar Oct 29 '18 11:10 drundanibel

Is there any progress on this?

drundanibel avatar Jan 15 '19 08:01 drundanibel

Hi All, similar issue i was facing and I came up with a work around for same. please try it .. The problem is with the header menu bar position is fixed , so solution is change the position as "absolute", below same code will help you.

WebElement element = driver.findElement(By.cssSelector("selector")); ((JavascriptExecutor) driver).executeScript("arguments[0].style.position='absolute'", element);

I hope it helps you.

rciddagoni avatar Nov 25 '19 14:11 rciddagoni

Hi All,

This may help you. Removing or changing the style property of the sticky header using JAVASCRIPT and taking the scrollable screenshot is not suggested as it is considered as standard way of testing and producing screenprint.

One best way is to use the JS and find the full page height viewport height, sticky header height and scroll and take the screenshot. You can use the below code.

public void scrollTheStickyHeaderPage(){
/** This function scrolls and takes screenshot **/
JavascriptExecutor js = (JavascriptExecutor) driver;  
int fullPageHeight = Integer.parseInt(js.executeScript
    ("return document.documentElement.ScrollHeight").toString());
int viewportHeight = Integer.parseInt(js.executeScript("return Math.max
    (document.documentElement.clientHeight, window.innerHeight || 0)").toString());
int headerHeight = Integer.parseInt(js.executeScript("return Math.max
    (document.getElementsByTagName('/**element**/')[0].clientHeight,
    document.getElementsByTagName('/**element**/')[0].offsetHeight,
    document.getElementsByTagName('/**element**/')[0].scrollHeight || 0)").toString());
int numofScrolls = (fullPageHeight/viewportHeight);
/** call your screenshot function **/
 for(int i=1; i<=numofScrolls; i++){
  js.executeScript("window.scrollBy(0,"+(viewportHeight-headerHeight)+")");
  /** call your screenshot function **/  
 }
}

PradheepRaaj avatar Dec 19 '19 14:12 PradheepRaaj

@rciddagoni Thank you for the above solution, it worked for a single element, but i have a full "div" which is sticky. do we have any solution for it???

shyamkumar1507 avatar Dec 24 '19 02:12 shyamkumar1507

@rciddagoni i could hide the nav totally using ((JavascriptExecutor) driver).executeScript("arguments[0].style.display='none'", element);. Thank you!!

shyamkumar1507 avatar Dec 25 '19 04:12 shyamkumar1507

I use this way to make header not fixed at the top. JavascriptExecutor js = (JavascriptExecutor)driver; js.executeScript("document.getElementsByClassName('residential-header sticky-header fixed')[0].classList.remove('fixed');");

lucas-nguyen-17 avatar Feb 06 '20 09:02 lucas-nguyen-17

我觉得可以加入opencv的去除重复的元素,但是我个人算法不行,无法提供代码!

cosoc avatar Nov 15 '22 09:11 cosoc

@cosoc could you please use English?

valfirst avatar Nov 16 '22 10:11 valfirst