3

I currently have a large number of circumstances where I need to verify that a page (along with all of its elements) are displaying correctly. The isDisplayed() method of WebElement appears to be a logical way to do this, however I would like to understand precisely what this method is doing to determine whether or not an element "is displayed". The javadoc does not shed any light on the inner workings of the method and other information on the web appears to be sparse at best.

If anyone could provide a detailed description of how this method works, I would be very grateful.

2 Answers 2

5

Mr. Spice, welcome to SQA. The algorithm is a little bit complicated, and I have never seen a complete explanation in written form. You need to take into account the element type; hidden, visibility, display attributes; opacity; dimensions; whether the element is hidden by overflow; the element's (x,y) position after applying coordinate transformations; and potentially some/all of those properties for ancestor elements.

Here is an excerpt from the Selenium source code:

/**
 * Determines whether an element is what a user would call "shown". This means
 * that the element is shown in the viewport of the browser, and only has
 * height and width greater than 0px, and that its visibility is not "hidden"
 * and its display property is not "none".
 * Options and Optgroup elements are treated as special cases: they are
 * considered shown iff they have a enclosing select element that is shown.
 *
 * @param {!Element} elem The element to consider.
 * @param {boolean=} opt_ignoreOpacity Whether to ignore the element's opacity
 *     when determining whether it is shown; defaults to false.
 * @return {boolean} Whether or not the element is visible.
 */
bot.dom.isShown = function(elem, opt_ignoreOpacity) {
  if (!bot.dom.isElement(elem)) {
    throw new Error('Argument to isShown must be of type Element');
  }

  // Option or optgroup is shown iff enclosing select is shown (ignoring the
  // select's opacity).
  if (bot.dom.isElement(elem, goog.dom.TagName.OPTION) ||
      bot.dom.isElement(elem, goog.dom.TagName.OPTGROUP)) {
    var select = /**@type {Element}*/ (goog.dom.getAncestor(elem, function(e) {
      return bot.dom.isElement(e, goog.dom.TagName.SELECT);
    }));
    return !!select && bot.dom.isShown(select, /*ignoreOpacity=*/true);
  }

  // Image map elements are shown if image that uses it is shown, and
  // the area of the element is positive.
  var imageMap = bot.dom.maybeFindImageMap_(elem);
  if (imageMap) {
    return !!imageMap.image &&
           imageMap.rect.width > 0 && imageMap.rect.height > 0 &&
           bot.dom.isShown(imageMap.image, opt_ignoreOpacity);
  }

  // Any hidden input is not shown.
  if (bot.dom.isElement(elem, goog.dom.TagName.INPUT) &&
      elem.type.toLowerCase() == 'hidden') {
    return false;
  }

  // Any NOSCRIPT element is not shown.
  if (bot.dom.isElement(elem, goog.dom.TagName.NOSCRIPT)) {
    return false;
  }

  // Any element with hidden visibility is not shown.
  if (bot.dom.getEffectiveStyle(elem, 'visibility') == 'hidden') {
    return false;
  }

  // Any element with a display style equal to 'none' or that has an ancestor
  // with display style equal to 'none' is not shown.
  function displayed(e) {
    if (bot.dom.getEffectiveStyle(e, 'display') == 'none') {
      return false;
    }
    var parent = bot.dom.getParentElement(e);
    return !parent || displayed(parent);
  }
  if (!displayed(elem)) {
    return false;
  }

  // Any transparent element is not shown.
  if (!opt_ignoreOpacity && bot.dom.getOpacity(elem) == 0) {
    return false;
  }

  // Any element without positive size dimensions is not shown.
  function positiveSize(e) {
    var rect = bot.dom.getClientRect(e);
    if (rect.height > 0 && rect.width > 0) {
      return true;
    }
    // A vertical or horizontal SVG Path element will report zero width or
    // height but is "shown" if it has a positive stroke-width.
    if (bot.dom.isElement(e, 'PATH') && (rect.height > 0 || rect.width > 0)) {
      var strokeWidth = bot.dom.getEffectiveStyle(e, 'stroke-width');
      return !!strokeWidth && (parseInt(strokeWidth, 10) > 0);
    }
    // Zero-sized elements should still be considered to have positive size
    // if they have a child element or text node with positive size, unless
    // the element has an 'overflow' style of 'hidden'.
    return bot.dom.getEffectiveStyle(e, 'overflow') != 'hidden' &&
        goog.array.some(e.childNodes, function(n) {
          return n.nodeType == goog.dom.NodeType.TEXT ||
                 (bot.dom.isElement(n) && positiveSize(n));
        });
  }
  if (!positiveSize(elem)) {
    return false;
  }

  // Elements that are hidden by overflow are not shown.
  if (bot.dom.getOverflowState(elem) == bot.dom.OverflowState.HIDDEN) {
    return false;
  }

  function isTransformHiding(e) {
    var transform = bot.dom.getEffectiveStyle(e, '-o-transform') ||
                    bot.dom.getEffectiveStyle(e, '-webkit-transform') ||
                    bot.dom.getEffectiveStyle(e, '-ms-transform') ||
                    bot.dom.getEffectiveStyle(e, '-moz-transform') ||
                    bot.dom.getEffectiveStyle(e, 'transform');

    // Not all browsers know what a transform is so if we have a returned value
    // lets carry on checking up the tree just in case. If we ask for the
    // transform matrix and look at the details there it will return the centre
    // of the element
    if (transform && transform !== "none") {
      var locOfElement = goog.style.getClientPosition(e);
      var rect = bot.dom.getClientRect(e);
      if ((locOfElement.x + (rect.width)) >= 0 &&
          (locOfElement.y + (rect.height)) >= 0){
        return true;
      } else {
        return false;
      }
    } else {
      var parent = bot.dom.getParentElement(e);
      return !parent || isTransformHiding(parent);
    }
  }
  return isTransformHiding(elem);
};
4
  • That's the piece of code that I started to look for Commented Aug 6, 2013 at 1:13
  • So in fact, isDisplayed() should be quite a reliable way to solve my problem?
    – Mr. Spice
    Commented Aug 6, 2013 at 11:31
  • Yes, I think so. Of course Selenium has bugs like everything else, and it needs updates from time to time to keep up with browser updates. You will have to decide for yourself whether it is reliable for your purposes.
    – user246
    Commented Aug 6, 2013 at 11:45
  • When testing real world enterprise apps, 99% of the time you end up with custom implementations for various elements. "isVisible" is just way to ambiguous on many levels. A reliable approach is to have an element inheritance hierarchy, have the base implementation use selenium's implementation, and override where needed with custom javascript snippets. Commented Jul 1, 2020 at 13:54
0

I work with the python binding, but the underlined behavior should be the same.

Functionally:

  • It would check if the webelement is hidden or not
  • It may check if the coordinates of the webelement are within the visible range of the user's browser

Code:

This is what I found for python:

The code for is_displayed() executes Command.IS_ELEMENT_DISPLAYED, which maps to isElementDisplayed in the command.Command class

Further taking a look here, isElementDisplayed is executed. But that's where I hit the wall - it executes isElementDisplayed() from which framework?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.