[ 
https://issues.apache.org/jira/browse/WICKET-6439?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Hendy Irawan updated WICKET-6439:
---------------------------------
    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 :)

!https://issues.apache.org/jira/secure/attachment/12880581/wicketscroll.jpg!

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[] :

{code:java}
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);
        }
    }

}
{code}

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.

  was:
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 :)

!https://issues.apache.org/jira/secure/attachment/12880581/wicketscroll.jpg!

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[] :

{code:java}
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);
        }
    }

}
{code}

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 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.


> Support auto-scroll for (Fenced)FeedbackPanel
> ---------------------------------------------
>
>                 Key: WICKET-6439
>                 URL: https://issues.apache.org/jira/browse/WICKET-6439
>             Project: Wicket
>          Issue Type: Improvement
>          Components: wicket
>    Affects Versions: 7.8.0
>            Reporter: Hendy Irawan
>            Priority: Minor
>         Attachments: wicketscroll.jpg
>
>
> 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 
> :)
> !https://issues.apache.org/jira/secure/attachment/12880581/wicketscroll.jpg!
> 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[] :
> {code:java}
> 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);
>         }
>     }
> }
> {code}
> 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.



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)

Reply via email to