How to turn off tab + space submitting a reply?

Great, please tell me, how am I supposed to do that? Didn’t I mention that hitting tab and then space happens by accident?

Yes, it is, but not if it happens by accident.

You’re arguing against conventions used across the internet.

I’m not really sure what you think that might achieve, Google isn’t going to stop sending blank emails any more than Discourse can guess when you’re done with a post and really intended to submit it.

Asking every accessibility user to tab past additional elements just because you occasionally post prematurely isn’t a solution. You’re going to find that submit buttons follow form elements across the internet. The standard of tab and space to navigate and select is defined by the W3C WAI ARIA, not us.

3 Likes

Specific accidental keypress accessibility issues might be better attacked at the OS level, for example:

https://help.gnome.org/users/gnome-help/stable/a11y-slowkeys.html.en

3 Likes

Thanks for the info. I will check if something similar is available for macOS, in which case I could just activate it when typing in discourse.

As I said, I never had this issue anywhere else, so Discourse is the only UI that allows me to post prematurely.
No issue in Thunderbird, Mediawiki editor, github UI, gitlab UI, vi, mutt, …

Or I just stick with writing the text in a text editor first.

I use tab then space all the time to submit in various online forms, it is expected that tab beyond the end of the form (our editor is one “field”) will take focus to the submit button.

5 Likes

Hmm, in this case it’s probably just coincidence that all the UIs I work with (except discourse) don’t focus submit at the first tab.

Anyway, I guess we can close this topic. Although, maybe @Stephen can test why the focus box isn’t shown in Firefox the way @awesomerobot described above.

Another alternative is to just use the composer in full-screen mode while writing your posts. Then the post/ reply button is removed, and you can’t hit it with an accidental tab+space.

4 Likes

For example I just tested this on Reddit. After typing anything the comment button is enabled, pressing tab you are taken to the comment box. space then submits.

5 Likes

Thanks for the tip.

P.S.: I need 20 characters to reply.

Sorry, I haven’t used reddit that often either. Here are a few examples that are contrary to this behavior are: github, mediawiki, Thunderbird, mutt, … (just to show that this is not necessarily the default in all UIs)

Could it be possible to make it so that space won’t do the submission, but it would submit only if return was hit? I think that would solve his problem.

This certainly would fix it. But I do understand that the devs don’t wan’t to change the UI just for me. But maybe there’s a hack or a plugin I can use…

Yes. But I’m suggesting that having space as a submission isn’t entirely expected and removing it likely wouldn’t hurt anyone.

Using space to submit is again a normal and expected keyboard navigation method to trigger a button in a GUI and web browser.

IMO the only improvement I could see making here is a more obvious highlight for the control with focus when using keyboard navigation.

7 Likes

Tab also moves focus to the submit on Stack Overflow, so pressing Space at this point would submit the post there as well.

1 Like

If you really wanted to you can override the handlebars template and change the tabindex value to come last (so it won’t be next after the textarea).

If you add all of this to admin > customize > themes > edit CSS/HTML > header it will do it. Just keep in mind that if we edit our default template you’ll miss out on the changes unless you update this to match it.

This is our default composer template with the reply button set to a tabindex of 9, which makes it last.

<script type="text/x-handlebars" data-template-name="composer">

{{#composer-body composer=model
                 showPreview=showPreview
                 openIfDraft=(action "openIfDraft")
                 typed=(action "typed")
                 cancelled=(action "cancelled")
                 save=(action "save")}}
  <div class="grippie"></div>
  {{#if visible}}
      {{composer-messages composer=model
                          messageCount=messageCount
                          addLinkLookup=(action "addLinkLookup")}}
      {{#if model.viewOpenOrFullscreen}}
        <div class="reply-area {{if canEditTags 'with-tags'}}">
          <div class='composer-fields'>
            {{plugin-outlet name="composer-open" args=(hash model=model)}}
            <div class='reply-to'>
              {{#unless model.viewFullscreen}}
                <div class="reply-details">
                  {{composer-action-title
                    model=model
                    openComposer=(action "openComposer")
                    closeComposer=(action "closeComposer")
                    canWhisper=canWhisper
                    tabindex=8}}
                  {{plugin-outlet name="composer-action-after" noTags=true args=(hash model=model)}}

                  {{#unless site.mobileView}}
                    {{#if isWhispering}}
                      <span class='whisper'>{{d-icon "far-eye-slash"}}</span>
                    {{/if}}
                    {{#if model.unlistTopic}}
                      <span class='whisper'>({{i18n 'composer.unlist'}})</span>
                    {{/if}}
                    {{#if model.noBump}}
                      <span class="no-bump">{{d-icon "anchor"}}</span>
                    {{/if}}
                  {{/unless}}

                  {{#if canEdit}}
                    {{#link-to-input onClick=(action "displayEditReason") showInput=showEditReason key="composer.show_edit_reason" class="display-edit-reason"}}
                      {{text-field value=editReason tabindex="7" id="edit-reason" maxlength="255" placeholderKey="composer.edit_reason_placeholder"}}
                    {{/link-to-input}}
                  {{/if}}
                </div>
              {{/unless}}
              {{composer-toggles composeState=model.composeState
                        toggleComposer=(action "toggle")
                        toggleToolbar=(action "toggleToolbar")
                        toggleFullscreen=(action "fullscreenComposer")}}
            </div>
            {{#unless model.viewFullscreen}}
              {{#if model.canEditTitle}}
                {{#if model.creatingPrivateMessage}}
                  <div class='user-selector'>
                    {{composer-user-selector topicId=topicModel.id
                                             usernames=model.targetUsernames
                                             hasGroups=model.hasTargetGroups
                                             focusTarget=focusTarget
                                             class="users-input"}}
                    {{#if showWarning}}
                      <label class='add-warning'>
                        {{input type="checkbox" checked=model.isWarning tabindex="3"}}
                        {{i18n "composer.add_warning"}}
                      </label>
                    {{/if}}
                  </div>
                {{/if}}

                <div class="title-and-category {{if showPreview 'with-preview'}}">

                  {{composer-title composer=model lastValidatedAt=lastValidatedAt focusTarget=focusTarget}}

                  {{#if model.showCategoryChooser}}
                    <div class="category-input">
                      {{category-chooser
                        fullWidthOnMobile=true
                        value=model.categoryId
                        scopedCategoryId=scopedCategoryId
                        tabindex="3"}}
                      {{popup-input-tip validation=categoryValidation}}
                    </div>
                  {{/if}}
                  {{#if canEditTags}}
                    {{mini-tag-chooser tags=model.tags tabindex="4" categoryId=model.categoryId minimum=model.minimumRequiredTags}}
                    {{popup-input-tip validation=tagValidation}}
                  {{/if}}
                </div>
              {{/if}}

              {{plugin-outlet name="composer-fields" args=(hash model=model)}}
            {{/unless}}

          </div>

          {{composer-editor topic=topic
                            composer=model
                            lastValidatedAt=lastValidatedAt
                            canWhisper=canWhisper
                            storeToolbarState=(action "storeToolbarState")
                            onPopupMenuAction=(action "onPopupMenuAction")
                            showUploadModal=(route-action "showUploadSelector")
                            popupMenuOptions=popupMenuOptions
                            draftStatus=model.draftStatus
                            isUploading=isUploading
                            allowUpload=allowUpload
                            uploadIcon=uploadIcon
                            isCancellable=isCancellable
                            uploadProgress=uploadProgress
                            groupsMentioned=(action "groupsMentioned")
                            cannotSeeMention=(action "cannotSeeMention")
                            importQuote=(action "importQuote")
                            togglePreview=(action "togglePreview")
                            showToolbar=showToolbar
                            afterRefresh=(action "afterRefresh")
                            focusTarget=focusTarget}}

          <div class='submit-panel'>
            {{plugin-outlet name="composer-fields-below" args=(hash model=model)}}

            <div class='save-or-cancel'>
              {{#unless model.viewFullscreen}}
                {{composer-save-button action=(action "save")
                                       icon=saveIcon
                                       label=saveLabel
                                       tabindex="9"
                                       disableSubmit=disableSubmit}}

              {{#if site.mobileView}}
                <a href {{action "cancel"}} class='cancel' tabindex="6" title="{{i18n 'cancel'}}">
                  {{#if canEdit}}
                    {{d-icon "times"}}
                  {{else}}
                    {{d-icon "far-trash-alt"}}
                  {{/if}}
                </a>
              {{else}}
                <a href {{action "cancel"}} class='cancel' tabindex="6" >{{i18n 'cancel'}}</a>
              {{/if}}
            {{/unless}}


              {{#if site.mobileView}}
                {{#if whisperOrUnlistTopic}}
                  <span class='whisper'>
                    {{d-icon "far-eye-slash"}}
                  </span>
                {{/if}}
                {{#if model.noBump}}
                  <span class="no-bump">{{d-icon "anchor"}}</span>
                {{/if}}
              {{/if}}


              {{#if isUploading}}
                <div id="file-uploading">
                  {{loading-spinner size="small"}}<span>{{i18n 'upload_selector.uploading'}} {{uploadProgress}}%</span>
                  {{#if isCancellable}}
                    <a href id="cancel-file-upload" {{action "cancelUpload"}}>{{d-icon "times"}}</a>
                  {{/if}}
                </div>
              {{/if}}
              <div id='draft-status' class="{{if isUploading 'hidden'}}">
                {{#if model.draftConflictUser}}
                  {{avatar model.draftConflictUser imageSize="small"}}
                {{/if}}
                {{model.draftStatus}}
              </div>
            </div>

            <div class="composer-bottom-right">
              {{#if site.mobileView}}
                {{#if allowUpload}}
                  <a class="btn btn-default no-text mobile-file-upload {{if isUploading 'hidden'}}">
                    {{d-icon uploadIcon}}
                  </a>
                {{/if}}

                {{#if showPreview}}
                  {{d-button action=(action "togglePreview") class="hide-preview" label="composer.hide_preview"}}
                {{/if}}
              {{else}}
                <a href {{action "togglePreview"}} class='toggle-preview'>{{{toggleText}}}</a>
              {{/if}}

            </div>
          </div>
        </div>

      {{else}}
        <div class='saving-text'>
          {{#if model.createdPost}}
            {{i18n 'composer.saved'}} <a class='permalink' href="{{unbound createdPost.url}}" {{action "viewNewReply"}}>{{i18n 'composer.view_new_post'}}</a>
          {{else}}
            {{i18n 'composer.saving'}} {{loading-spinner size="small"}}
          {{/if}}
        </div>

        <div class='draft-text'>
          {{#if model.topic}}
            {{d-icon "share"}} {{{draftTitle}}}
          {{else}}
            {{i18n "composer.saved_draft"}}
          {{/if}}
        </div>

        {{composer-toggles composeState=model.composeState
          toggleFullscreen=(action "openIfDraft")
          toggleComposer=(action "toggle")
          toggleToolbar=(action "toggleToolbar")}}

      {{/if}}

  {{/if}}

{{/composer-body}}

</script>

4 Likes

Thanks for that idea. Unfortunately I can’t do this as a user.

As mentioned before, I think we can close this topic.
It was news to me that tab and space is a thing to submit text, especially since I don’t use reddit or stackoverflow often enough to have noticed that they behave the same way.
All the UIs I use on a daily basis don’t do that, so after I started to use discourse more often, I noticed this behavior.

I’m a fully aware that the industry is not going to change its way.

All I am saying is that it is easy to invoke Submit by accident (which in my opinion shouldn’t be the case - accessibility or not. It’s even easier to make this mistake when you are disabled and can only use one hand, thus accessibilty features should make your life easier not harder).

4 Likes