使用 Java 的 Selenium WebDriver 和 HTML 窗口位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14655581/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 16:59:09  来源:igfitidea点击:

Selenium WebDriver and HTML Window location by using Java

javaseleniumselenium-webdriver

提问by Merkidemis

I am using Selenium WebDriver in conjunction with java.awt.Robot to better simulate user interaction with our web application. Yes, I know it's probably unnecessary, but the customers I serve demand it.

我将 Selenium WebDriver 与 java.awt.Robot 结合使用,以更好地模拟用户与我们的 Web 应用程序的交互。是的,我知道这可能是不必要的,但我服务的客户需要它。

Currently things are working pretty well, however I have a minor snag in that I can't seem to find a good way to get the web elements' on screen position. Things like the title bar, menu bar, navigation bar, etc all push the content down on the physical screen (which Robot gets its coordinates from), but has no impact on where Selenium reports the element is.

目前一切运行良好,但是我有一个小问题,我似乎无法找到一种让 web 元素在屏幕上的位置的好方法。标题栏、菜单栏、导航栏等都将内容向下推到物理屏幕上(Robot 从中获取坐标),但对 Selenium 报告元素的位置没有影响。

When I call: element.getLocation();on a Selenium WebElement, it always gives me its location relative to the HTML content render pane, not the browser window itself.

当我调用:element.getLocation();在 Selenium WebElement 上时,它总是给我它相对于 HTML 内容呈现窗格的位置,而不是浏览器窗口本身。

A better illustration is: driver.findElement(By.tagName("body")).getLocation();always returns 0,0, regardless of the window's actual on-screen location.

更好的说明是:driver.findElement(By.tagName("body")).getLocation();无论窗口在屏幕上的实际位置如何,始终返回 0,0。

Right now I am hacking it by adding a vertical and horizontal offset after maximizing the window, but these aren't the same between different browsers (IE's top decorations take up more room then Firefox's, for example), and may be different for each user if they have bookmark tool bars, search bars, etc added in.

现在我通过在最大化窗口后添加垂直和水平偏移来破解它,但这些在不同浏览器之间并不相同(例如,IE 的顶部装饰比 Firefox 占据更多空间),并且每个用户可能会有所不同如果他们添加了书签工具栏、搜索栏等。

Yes, I know I could run in full screen mode, but I'd rather not, if at all possible.

是的,我知道我可以在全屏模式下运行,但如果可能的话,我宁愿不这样做。

Is there a way to use WebDriver to get the physical on-screen location of elements in a reliable manner?

有没有办法使用 WebDriver 以可靠的方式获取元素在屏幕上的物理位置?

采纳答案by Petr Jane?ek

I believe there's no way to get the real on-screen location of the elements on the page.

我相信没有办法获得页面元素在屏幕上的真实位置。

I also think the fullscreen mode is your best bet.

我也认为全屏模式是你最好的选择。

That said, I wrote a RobotCalibrationclass that can detect the real offsets of your current browser. It opens a specially crafted page and uses the Robotclass to click on it. The algorithm starts in the center of the browser and then uses bisecting to find the top left corner of the browser viewport.

也就是说,我编写了一个RobotCalibration可以检测当前浏览器实际偏移量的类。它打开一个特制的页面并使用Robot类来点击它。该算法从浏览器的中心开始,然后使用二分法找到浏览器视口的左上角。

Tested on IE8 and FF18. Works for both maximized and windowed browsers. Known issue: If you have a top Bookmarks Toolbar enabled, it may click on some of the bookmarks and therefore redirect. It can be handled pretty easily, but I left it up to you if you needed that :).

在 IE8 和 FF18 上测试。适用于最大化和窗口化浏览器。已知问题:如果您启用了顶部书签工具栏,它可能会点击某些书签并因此重定向。它可以很容易地处理,但如果你需要,我把它留给你:)。

The testing page:

测试页面:

<!DOCTYPE html>
<html lang="en" onclick="document.getElementById('counter').value++">
<head>
    <meta charset="utf-8" />
    <title>Calibration Test</title>
</head>
<body>
    <img height="1" width="1" style="position: absolute; left: 0; top: 0;"
        onclick="document.getElementById('done').value = 'yep'" />
    <input type="text" id="counter" value="0" />
    <input type="text" id="done" value="nope" />
</body>
</html>

The RobotCalibrationclass. It's a little bit long, so I suggest you to copypaste it into your favourite IDE and explore it there:

RobotCalibration班。它有点长,所以我建议您将其复制粘贴到您最喜欢的 IDE 中并在那里进行探索:

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.nio.file.Paths;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;

public class RobotCalibration {

    public static Point calibrate(WebDriver driver) {
        return new RobotCalibration(driver).calibrate();
    }

    /** Time for which to wait for the page response. */
    private static final long TIMEOUT = 1000;

    private final WebDriver driver;
    private final Robot r;

    private final Point browserCenter;
    private int leftX;
    private int rightX;
    private int midX;
    private int topY;
    private int bottomY;
    private int midY;

    private RobotCalibration(WebDriver driver) {
        this.driver = driver;
        try {
            driver.manage().window().getSize();
        } catch (UnsupportedOperationException headlessBrowserException) {
            throw new IllegalArgumentException("Calibrating a headless browser makes no sense.", headlessBrowserException);
        }

        try {
            this.r = new Robot();
        } catch (AWTException headlessEnvironmentException) {
            throw new IllegalStateException("Robot won't work on headless environments.", headlessEnvironmentException);
        }

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        org.openqa.selenium.Dimension browserSize = driver.manage().window().getSize();
        org.openqa.selenium.Point browserPos = driver.manage().window().getPosition();

        // a maximized browser returns negative position
        // a maximized browser returns size larger than actual screen size
        // you can't click outside the screen
        leftX = Math.max(0, browserPos.x);
        rightX = Math.min(leftX + browserSize.width, screenSize.width - 1);
        midX = (leftX + rightX) /2;

        topY = Math.max(0, browserPos.y);
        bottomY = Math.min(topY + browserSize.height, screenSize.height - 1);
        midY = (topY + bottomY) /2;

        browserCenter = new Point(midX, midY);
    }

    private Point calibrate() {
        driver.get(Paths.get("files/RobotCalibration.html").toUri().toString());

        // find left border
        while (leftX < rightX) {
            click(midX, midY);
            if (clickWasSuccessful()) {
                rightX = midX;
            } else {
                leftX = midX + 1;
                // close any menu we could have opened
                click(browserCenter.x, browserCenter.y);
            }
            midX = (leftX + rightX) /2;
        }

        // find top border
        while (topY < bottomY) {
            click(midX, midY);
            if (clickWasSuccessful()) {
                bottomY = midY;
            } else {
                topY = midY + 1;
                // close any menu we could have opened
                click(browserCenter.x, browserCenter.y);
            }
            midY = (topY + bottomY) /2;
        }

        if (!isCalibrated()) {
            throw new IllegalStateException("Couldn't calibrate the Robot.");
        }
        return new Point(midX, midY);
    }

    /** clicks on the specified location */
    private void click(int x, int y) {
        r.mouseMove(x, y);
        r.mousePress(InputEvent.BUTTON1_DOWN_MASK);
        r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);

        // for some reason, my IE8 can't properly register clicks that are close
        // to each other faster than click every half a second
        if (driver instanceof InternetExplorerDriver) {
            sleep(500);
        }
    }

    private static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ignored) {
            // nothing to do
        }
    }

    private int counter = 0;
    /** @return whether the click on a page was successful */
    private boolean clickWasSuccessful() {
        counter++;

        long targetTime = System.currentTimeMillis() + TIMEOUT;
        while (System.currentTimeMillis() < targetTime) {
            int pageCounter = Integer.parseInt(driver.findElement(By.id("counter")).getAttribute("value"));
            if (counter == pageCounter) {
                return true;
            }
        }
        return false;
    }

    /** @return whether the top left corner has already been clicked at */
    private boolean isCalibrated() {
        long targetTime = System.currentTimeMillis() + TIMEOUT;
        while (System.currentTimeMillis() < targetTime) {
            if (driver.findElement(By.id("done")).getAttribute("value").equals("yep")) {
                return true;
            }
        }
        return false;
    }

}

Sample usage:

示例用法:

WebDriver driver = new InternetExplorerDriver();
Point p = RobotCalibration.calibrate(driver);
System.out.println("Left offset: " + p.x + ", top offset: " + p.y);
driver.quit();

Feel free to ask any questions if something is unclear.

如果有不清楚的地方,请随时提出任何问题。

回答by Josh Partridge

It looks like selenium has a way to get the element position relative to your browser window now. In one of my tests I was trying to verify that a page was anchored to a certain position and figured the best way to do this was to get an element position relative to the window. This can be done using the getCoordinates().inViewPort()methods.

看起来 selenium 现在有一种方法可以获取相对于浏览器窗口的元素位置。在我的一次测试中,我试图验证页面是否锚定到某个位置,并认为最好的方法是获取相对于窗口的元素位置。这可以使用getCoordinates().inViewPort()方法来完成。

Here's some sample code to get a WebElement's relative location:

下面是一些获取 WebElement 相对位置的示例代码:

import org.openqa.selenium.By;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.internal.Locatable;

public class RelativeElementLocation {

private static Point locationPoint;
private static Locatable elementLocation;
WebDriver driver = new FirefoxDriver();
WebElement element;

public Point location() throws Throwable {
    driver.get("Insert your URL here");
    element = driver.findElement(By.id("Insert id/css/xpath/ here"));
    elementLocation = (Locatable) element;
    locationPoint = elementLocation.getCoordinates().inViewPort();
    System.out.println(locationPoint);

    return locationPoint; 
    }
}

The only caveat to the inViewPort()method is that if you try to get the relative element location of an element that currently is not in page view, the method will scroll the page so that the element is in view and then give you the relative position. Meaning, say if you try to get an element's relative position that is above your browser page view, it's not going to give you a negative y coordinate, but rather scroll up to that element and make the y coordinate zero.

inViewPort()方法的唯一警告是,如果您尝试获取当前不在页面视图中的元素的相对元素位置,该方法将滚动页面以便该元素在视图中,然后为您提供相对位置位置。意思是,如果您尝试获取浏览器页面视图上方元素的相对位置,它不会为您提供负 y 坐标,而是向上滚动到该元素并使 y 坐标为零。

Hope this helps, I'm new to Selenium and Java so if there are any mistakes in my code please forgive me.

希望这会有所帮助,我是 Selenium 和 Java 的新手,所以如果我的代码中有任何错误,请原谅我。

回答by McX

There's a simpler way than Slanec answerto get relative to absolute offset.

有一种比Slanec 答案更简单的方法来获得相对于绝对偏移量。

First you need a blank page with javascript attached click listeneron window to get the coordinates and save them into a global variable :

首先,您需要一个空白页面,在窗口上附加有 javascript 的单击侦听器,以获取坐标并将它们保存到全局变量中:

window.coordinates = [];
window.attachEvent('click', function() {
     window.coordinates = [event.pageX, event.pageY];
});

Then you click and get relative click position through JavascriptExecutor:

然后您点击并通过JavascriptExecutor以下方式获得相对点击位置:

// browser center
Dimension size = driver.manage().window().getSize();
Point point = driver.manage().window().getPosition()
    .translate(size.width / 2, size.height / 2);

// Click through robot
click(point.x, point.y);
Thread.sleep(500);

// read coordinates
List<Object> coordinates = (List<Object>) ((JavascriptExecutor) driver)
    .executeScript("return window.coordinates;");
if (coordinates.size() != 2)
     throw new IllegalStateException("Javascript did not receive the click");

// point contains screen coordinates of upper left page point
point.translate(-((Integer)coordinates.get(0)),
                -((Integer)coordinates.get(1)));

回答by Terence Xie

The current Selenium doesn't implement the API to get the absolute location of web element. But the SAFShas implemented one version to get the absolute location of web element.

当前的 Selenium 没有实现获取 web 元素绝对位置的 API。但是SAFS已经实现了一个版本来获取 web 元素的绝对位置。

The thinking is below:

思路如下:

  1. Get the element's location relativeto the frame. Because there may be lots of frames in one browser web page. Also, the frame may be implanted in another frame. So, it's better to use a class with parent frame to recursively search to the outer most frame location.
  1. 获取元素对于框架的位置。因为一个浏览器网页中可能有很多框架。此外,该框架可以植入到另一个框架中。因此,最好使用具有父框架的类来递归搜索到最外层的框架位置。

`

`

class FrameElement {
    FrameElement parent;
    Point location;

    FrameElement(FrameElement parent, WebElement frame) throws ... {
        if(parent != null) {
            this.parent = parent;
            location = frame.getLocation().moveBy(parent.getLocation().x, parent.getLocation().y);
        } else {
            location = frame.getLocation();
        }
    }

    public Point getLocation() {return location;} 
}

Point p = webelement.getLocation();
  1. Add frame's location, relative to browser client area(the part exclude toolbar, menu, etc.).

  2. Add the browser client area, relative to browser window.

  3. Add the browser window's location, relative to screen.

  1. 添加框架的位置,相对于浏览器客户区(该部分不包括工具栏、菜单等)。

  2. 添加浏览器客户区,相对于浏览器窗口。

  3. 添加浏览器窗口相对于屏幕的位置。