@@ -191,23 +191,9 @@ public boolean isFocused() {
191191
192192 @ Override
193193 public void click () {
194- // JS call to click does not focus element, hence ensure focus
195- focus ();
196- try {
197- // Avoid strange "element not clickable at point" problems
198- callFunction ("click" );
199- } catch (Exception e ) {
200- if (e .getMessage ()
201- .contains ("Inspected target navigated or closed" )) {
202- // This happens with chromedriver although e.g. navigation
203- // succeeds
204- return ;
205- }
206- // SVG elements and maybe others do not have a 'click' method
207- autoScrollIntoView ();
208- waitForVaadin ();
209- wrappedElement .click ();
210- }
194+ autoScrollIntoView ();
195+ waitForVaadin ();
196+ new Actions (getDriver ()).click (wrappedElement ).build ().perform ();
211197 }
212198
213199 @ Override
@@ -363,12 +349,20 @@ public void click(int x, int y, Keys... modifiers) {
363349 actions .build ().perform ();
364350 }
365351
352+ /**
353+ * Performs a double-click action on this element.
354+ *
355+ * @see click()
356+ */
366357 public void doubleClick () {
367358 autoScrollIntoView ();
368359 waitForVaadin ();
369360 new Actions (getDriver ()).doubleClick (wrappedElement ).build ().perform ();
370361 }
371362
363+ /**
364+ * Performs a context-click (right click) action on this element.
365+ */
372366 public void contextClick () {
373367 autoScrollIntoView ();
374368 waitForVaadin ();
@@ -520,7 +514,20 @@ public boolean compareScreen(BufferedImage reference, String referenceName)
520514 (TakesScreenshot ) this , (HasCapabilities ) getDriver ());
521515 }
522516
523- /***
517+ /**
518+ * Scrolls the element into the visible area of the browser window with the
519+ * given options. Check
520+ * https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
521+ * for more information on the options.
522+ *
523+ * @param options
524+ * the parameters for scrolling into view
525+ */
526+ public void scrollIntoView (Map <String , Object > options ) {
527+ callFunction ("scrollIntoView" , options );
528+ }
529+
530+ /**
524531 * Scrolls the element into the visible area of the browser window
525532 */
526533 public void scrollIntoView () {
@@ -534,15 +541,65 @@ public void scrollIntoView() {
534541 */
535542 private void autoScrollIntoView () {
536543 try {
537- if (getCommandExecutor ().isAutoScrollIntoView ()) {
538- if (!wrappedElement .isDisplayed ()) {
539- scrollIntoView ();
540- }
544+ if (getCommandExecutor ().isAutoScrollIntoView ()
545+ && !isElementInViewport ()) {
546+ var params = Map .<String , Object > of ("block" , "end" , "inline" ,
547+ "end" );
548+ scrollIntoView (params );
541549 }
542550 } catch (Exception e ) {
543551 }
544552 }
545553
554+ private boolean isElementInViewport () {
555+ try {
556+ Boolean result = (Boolean ) executeScript (
557+ """
558+ function isVisible(elem) {
559+ // Check if element has zero dimensions (truly hidden)
560+ if (!elem.offsetParent && elem.offsetWidth === 0 && elem.offsetHeight === 0) {
561+ return false;
562+ }
563+
564+ var rect = elem.getBoundingClientRect();
565+ if (rect.width === 0 || rect.height === 0) {
566+ return false;
567+ }
568+
569+ // Check if element intersects with viewport
570+ var windowHeight = window.innerHeight || document.documentElement.clientHeight;
571+ var windowWidth = window.innerWidth || document.documentElement.clientWidth;
572+ if (rect.bottom < 0 || rect.top > windowHeight || rect.right < 0 || rect.left > windowWidth) {
573+ return false;
574+ }
575+
576+ // Check if clipped by any scrollable ancestor
577+ var parent = elem.parentElement;
578+ while (parent && parent !== document.body) {
579+ var style = window.getComputedStyle(parent);
580+ var overflow = style.overflow + style.overflowX + style.overflowY;
581+
582+ if (overflow.includes('hidden') || overflow.includes('scroll') || overflow.includes('auto')) {
583+ var parentRect = parent.getBoundingClientRect();
584+ // Check if element is outside parent's visible area
585+ if (rect.bottom < parentRect.top || rect.top > parentRect.bottom ||
586+ rect.right < parentRect.left || rect.left > parentRect.right) {
587+ return false;
588+ }
589+ }
590+ parent = parent.parentElement;
591+ }
592+ return true;
593+ }
594+ return isVisible(arguments[0]);
595+ """ ,
596+ this );
597+ return result != null && result ;
598+ } catch (Exception e ) {
599+ return true ; // Assume visible on error
600+ }
601+ }
602+
546603 /**
547604 * Waits the given number of seconds for the given condition to become
548605 * neither null nor false. {@link NotFoundException}s are ignored by
0 commit comments