Uploaded image for project: 'Wicket'
  1. Wicket
  2. WICKET-6439

Support auto-scroll for (Fenced)FeedbackPanel

    XMLWordPrintableJSON

Details

    • Improvement
    • Status: Closed
    • Minor
    • Resolution: Invalid
    • 7.8.0
    • None
    • wicket
    • None

    Description

      My use case is exactly the same as described in https://stackoverflow.com/questions/27172854/how-to-automatically-scroll-up-a-wicket-panel-when-wicket-feedbackpanel-is-trigg

      Allow me to elaborate.

      Assuming a mobile browser or any viewport size that can't display the full page: When user clicks the (ajax) Submit button and there's error, without this functionality, there are two scenarios:

      1. If the FeedbackPanel is on top, the user won't see it. Unless it's a stateless form, which then goes back to top. Either way they still have to scroll up or down to find the field.
      2. If the FeedbackPanel is right above the button, only users with working Ajax will see it and only for ajax forms. Else it back to top and they won't see it. Either way they still have to scroll up or down to find the field.

      Some users find this disorienting and I suspect this is the cause some people leave and never come back.

      Just for the sake of argument here's one goal flow (almost half of the users go away on the very first form) that I'd like to increase its conversion rate

      Proposed solution is simple, scroll to the FeedbackPanel message, which is optimally placed right on top of the first offending field (in case there are several). But some problems I encountered while trying to get solve this:

      1. With Form.onError() it's trivial to do this when there's only one FeedbackPanel. But with multiple {{FencedFeedbackPanel}}s there needs to be some logic to (1) know which of the FeedbackPanels gets which message, and (2) determine the "top-most" (based on component hierarchy), before (3) appending JavaScript to scroll there.
      2. Even with point 1 solved, I still need to duplicate that logic on every form on every page. It'd be great if this logic can be put directly in the FeedbackPanel itself. I've tried it using hint from https://stackoverflow.com/questions/19246892/how-to-get-the-ajaxrequesttarget-inside-wickets-onbeforerender-method-of-a-co but I can't even get any messages, any method I try simply gave either null or Array[] :

      public class ScrollNotificationPanel extends NotificationPanel {
          @Override
          protected void onInitialize() {
              super.onInitialize();
              setOutputMarkupId(true);
          }
      
          @Override
          protected void onAfterRender() {
              super.onAfterRender();
              final AjaxRequestTarget target = getRequestCycle().find(AjaxRequestTarget.class);
              if (null != target) {
                  final Object modelObject = newFeedbackMessagesModel().getObject();
      //            final List<FeedbackMessage> feedbackMessages = getFeedbackMessagesModel().getObject();
                  final String msgs = JsonUtils.asJson(modelObject);
                  target.appendJavaScript("console.log('horaXXXXXy', " + msgs + ");");
              }
              if (null != target && getFeedbackMessages().hasMessage(FeedbackMessage.ERROR)) {
                  String scrollScript = "$('html, body').animate({scrollTop:$('#" + getMarkupId() + "').offset().top - 20}, 'slow');\n";
                  target.appendJavaScript(scrollScript);
              }
          }
      
          @Override
          protected void onBeforeRender() {
              super.onBeforeRender();
              final AjaxRequestTarget target = getRequestCycle().find(AjaxRequestTarget.class);
              if (null != target) {
                  final Object modelObject = newFeedbackMessagesModel().getObject();
      //            final List<FeedbackMessage> feedbackMessages = getFeedbackMessagesModel().getObject();
                  final String msgs = JsonUtils.asJson(modelObject);
                  target.appendJavaScript("console.log('horay', " + msgs + ");");
              }
              if (null != target && getFeedbackMessages().hasMessage(FeedbackMessage.ERROR)) {
                  String scrollScript = "$('html, body').animate({scrollTop:$('#" + getMarkupId() + "').offset().top - 20}, 'slow');\n";
                  target.appendJavaScript(scrollScript);
              }
          }
      
      }
      

      3. There needs to be some "buffer" on top when scrolling. For example, most websites have a floating navbar on top, and scrolling right to a panel will make the panel covered by the navbar. So there should be a buffer value, 20 is a sane minimal value for comfort, but some websites (like mine) will need to adjust it to 50 or something else.

      4. Added bonus if this can work with non-JavaScript (i.e. Button/AjaxFallbackButton) / stateless forms. Technically it's possible by using the URL fragment, i.e. FeedbackPanel's outputMarkupId will need to be true, and the "next" page need to redirect to a fragment. However I have no idea how to do this robustly.

      (5. I know an edge case: horizontal scrolling, yet I personally don't care because IMHO if there's a horizontal scroll visible the page needs to fix its UX first.)

      So I hope you accept this proposal or at least give me hints on how to do point 1 & 2 above. If this gets implemented in Wicket this should be a flag instead of a new component, and I hope it's enabled by default (if not in Wicket 7 then hopefully for Wicket 8).

      Thank you in advance Martin!

      I just did a browser test and:

      1. The only method that partially works on Opera Mini is location.hash. Problem: 1. it only works on first submit; 2. if there's a floating navbar it will get covered on modern browsers, but not in Opera Mini that doesn't support floating navbar. Neither .scrollTop nor jQuery.scrollTo works (https://github.com/flesler/jquery.scrollTo/issues/158)
      2. On modern browsers $('html, body').animate({scrollTop works reliably. https://github.com/flesler/jquery.scrollTo might be used for extra features that some people want, but I don't think you'd like that bundled with Wicket, so basic jQuery method works very well.

      For me, I'd probably browser sniff Opera Mini and serve location.hash, else animate-scrollTop.

      Attachments

        1. wicketscroll.jpg
          32 kB
          Hendy Irawan

        Activity

          People

            svenmeier Sven Meier
            ceefour Hendy Irawan
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: