13

I'm setting up a questionnaire for clients that needs to be mobile-friendly.

When the form is submitted/validated, and a question is not answered, it scrolls to the first invalid field - whether it is a text input or radio input. This is only on desktop browsers and Android.

On iOS Safari/Chrome, the text input works as is should, just like on desktop browsers and Android.

But for radio inputs, it focuses on the question that hasn't been answered and shows the validation error message, but does not scroll up to the focused question if it is not in the current display.

This question has been asked, but was never answered. Is there a workaround for this?

        <form name="myForm" id="myForm"method="post">  
            <div class="form-row" id="info">
                <div class="col-md-3 mb-3">
                    <label for="name">Name</label>
                    <input type="text" class="form-control" id="name" placeholder="Name" value="Tony Stark" required>
                </div> <!-- NAME -->
                <div class="col-md-3 mb-3">
                    <label for="company">Company</label>
                    <input type="text" class="form-control" id="company" placeholder="Company" value="Stark Industries">
                </div> <!-- COMPANY -->
                <div class="col-md-3 mb-3">
                    <label for="address">Address</label>
                    <input type="text" class="form-control" id="address" placeholder="Address" value="10880 Malibu Point" required>
                </div> <!-- ADDRESS -->
                <div class="col-md-3 mb-3">
                    <label for="address2">Apt / Suite / Other (Optional)</label>
                    <input type="text" class="form-control" id="address2" placeholder="Apt / Suite / Other">
                </div> <!-- ADDRESS2 -->
            </div>

            <div class="form-row" id="citystzip">
                <div class="col-md-6 mb-3">
                    <label for="city">City</label>
                    <input type="text" class="form-control" id="city" placeholder="City" value="Malibu" required>
                </div> <!-- CITY -->

                <div class="col-md-3 mb-3">
                    <label for="state">State</label>
                    <select class="form-control" id="state" placeholder="State" value="CA" required>
                        <option value="AL">AL</option>
                        <option value="AK">AK</option>
                        <option value="AZ">AZ</option>
                        <option value="AR">AR</option>
                        <option value="CA">CA</option>
                        <option value="CO">CO</option>
                        <option value="CT">CT</option>
                        <option value="DE">DE</option>
                        <option value="DC">DC</option>
                        <option value="FL">FL</option>
                        <option value="GA">GA</option>
                        <option value="HI">HI</option>
                        <option value="ID">ID</option>
                        <option value="IL">IL</option>
                        <option value="IN">IN</option>
                        <option value="IA">IA</option>
                        <option value="KS">KS</option>
                        <option value="KY">KY</option>
                        <option value="LA">LA</option>
                        <option value="ME">ME</option>
                        <option value="MD">MD</option>
                        <option value="MA">MA</option>
                        <option value="MI">MI</option>
                        <option value="MN">MN</option>
                        <option value="MS">MS</option>
                        <option value="MO">MO</option>
                        <option value="MT">MT</option>
                        <option value="NE">NE</option>
                        <option value="NV">NV</option>
                        <option value="NH">NH</option>
                        <option value="NJ">NJ</option>
                        <option value="NM">NM</option>
                        <option value="NY">NY</option>
                        <option value="NC">NC</option>
                        <option value="ND">ND</option>
                        <option value="OH">OH</option>
                        <option value="OK">OK</option>
                        <option value="OR">OR</option>
                        <option value="PA">PA</option>
                        <option value="RI">RI</option>
                        <option value="SC">SC</option>
                        <option value="SD">SD</option>
                        <option value="TN">TN</option>
                        <option value="TX">TX</option>
                        <option value="UT">UT</option>
                        <option value="VT">VT</option>
                        <option value="VA">VA</option>
                        <option value="WA">WA</option>
                        <option value="WV">WV</option>
                        <option value="WI">WI</option>
                        <option value="WY">WY</option>
                    </select>
                </div> <!-- STATE -->
                <div class="col-md-3 mb-3">
                    <label for="zip">Zip</label>
                    <input type="text" class="form-control" id="zip" placeholder="Zip" value="90265" required>
                </div> <!-- ZIP -->
            </div>

            <div class="form-row" id="contact">
                <div class="col-md-7 mb-3">
                    <label for="email">Email</label>
                    <input type="email" class="form-control" id="email" placeholder="Email" value="tony@starkindustries.com" required>
                </div> <!-- EMAIL -->

                <div class="col-md-5 mb-3">
                    <label for="phone">Phone Number</label>
                    <input type="tel" class="form-control" id="phone" placeholder="Phone Number" value="678-136-7092" required>
                </div> <!-- PHONE -->
            </div>


        <h1>Please answer all survey questions and click submit at the bottom to submit <br>your updated information and survey answers.</h1>
            <legend>1. Question 1</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation1" name="question1" value="1a" required>
                <label class="custom-control-label" for="customControlValidation1">1a</label>
            </div>
            <div class="custom-control custom-radio mb-3">
                <input type="radio" class="custom-control-input" id="customControlValidation2" name="question1" value="1b" required>
                <label class="custom-control-label" for="customControlValidation2">1b</label>
            </div>

            <legend>2. Question 2</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation3" name="question2"  value="2a" required>
                <label class="custom-control-label" for="customControlValidation3">2a</label>
            </div>
            <div class="custom-control custom-radio mb-3">
                <input type="radio" class="custom-control-input" id="customControlValidation4" name="question2" value="2b" required>
                <label class="custom-control-label" for="customControlValidation4">2b</label>
            </div>

            <legend>3. Question 3</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation5" name="question3" value="3a" required>
                <label class="custom-control-label" for="customControlValidation5">3a</label>
            </div>
            <div class="custom-control custom-radio mb-3">
                <input type="radio" class="custom-control-input" id="customControlValidation6" name="question3" value="3b" required>
                <label class="custom-control-label" for="customControlValidation6">3b</label>
            </div>

            <legend>4. Question 4</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation7" name="question4" value="4a" required>
                <label class="custom-control-label" for="customControlValidation7">4a</label>
            </div>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation8" name="question4" value="4b" required>
                <label class="custom-control-label" for="customControlValidation8">4b</label>
            </div>


            <legend>5. Question 5</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation9" name="question5" value="5a" required>
                <label class="custom-control-label" for="customControlValidation9">5a</label>
            </div>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation10" name="question5" value="5b" required>
                <label class="custom-control-label" for="customControlValidation10">5b</label>
            </div>

            <legend>6. Question 6</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation11" name="question6" value="6a" required>
                <label class="custom-control-label" for="customControlValidation11">6a</label>
            </div>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation12" name="question6" value="6b" required>
                <label class="custom-control-label" for="customControlValidation12">6b</label>
            </div>

            <legend>7. Question 7</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation13" name="question7" value="7a" required>
                <label class="custom-control-label" for="customControlValidation13">7a</label>
            </div>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation14" name="question7" value="7b" required>
                <label class="custom-control-label" for="customControlValidation14">7b</label>
            </div>

            <legend>8. Question 8</legend>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation15" name="question8" value="8a" required>
                <label class="custom-control-label" for="customControlValidation15">8a</label>
            </div>
            <div class="custom-control custom-radio">
                <input type="radio" class="custom-control-input" id="customControlValidation16" name="question8" value="8b" required>
                <label class="custom-control-label" for="customControlValidation16">8b</label>
            </div>
Marina
  • 161
  • 8

5 Answers5

2

Since OP is still looking for a workaround and I was actively looking for a solution, here is my current workaround:

First I test if an iPhone/iPad is used (please check the note below), then attach EventListeners to the radio and checkbox inputs that monitor the focus event. Since the focus is set correctly on the first invalid input, the only thing left to do is to scroll that input into the visible area.

if (/^iPad|iPhone$/.test(navigator.platform)) {
    var inputs = form.querySelectorAll('input[type="radio"], input[type="checkbox"]');
    inputs.forEach(i => {
        i.addEventListener('focus', function (e) {
            e.target.scrollIntoView({
                block: 'center',
                behavior: "smooth"
            });
        })
    })
}

Note: Since iPadOS (/^iPad|iPhone$/.test(navigator.platform)) doesn't seem to work anymore. Tell iPadOS from macOS on the web lists solutions for this, but since I currently don't have an iPad which is able to run iPadOS I couldn't test them yet.

coreuter
  • 3,331
  • 4
  • 28
  • 74
0

With JQuery you can use

$.scrollTo($('#myDiv'), 1000)

Jark
  • 65
  • 1
  • 8
  • 2
    There are certainly many ways to do it with javascript/jquery. Hoping there's a solution that makes it work in Safari without needing custom code. Maybe there isn't, but .. seems crazy that Safari works for all other input types and just has let this gaping bug sit for years on radio buttons. – Dave Apr 15 '21 at 19:58
0
<input onFocus={(e) => {
                                                                document.getElementsByName(e.target.name)[0].scrollIntoView({
                                                                    behavior: 'smooth',
                                                                    block: 'center',
                                                                    inline: 'start',
                                                                });
                                                            }}
Abhay Phougat
  • 280
  • 2
  • 6
0

Here is a workaround I did without affecting other form behaviours. Since the radio element will be focused when validation fail, the idea is to scroll to the radio element if it is not in viewport.

/// fix iOS not auto scroll to radio input when validation fail
function isElementInViewport(el) {
    //https://stackoverflow.com/questions/123999/how-can-i-tell-if-a-dom-element-is-visible-in-the-current-viewport
    // Special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        /* or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement
            .clientWidth) /* or $(window).width() */
    );
}
//change form-check-input to your radio input class
$(".form-check-input").focus(function() {
    if (!isElementInViewport(this)) {
        document.getElementById(this.id).scrollIntoView();
    }
});
Hans
  • 17
  • 5
0

For Safari 16, I used to following code to both scroll + display the native error message:

$form = jQuery( /* ... */ );
if (!$form[0].checkValidity()) {
    // Fix to show elements on Safari
    $form.find('input, select').on('focus.showValidationErrors', (e) => {
        e.target.parentNode.scrollIntoView({
          block: 'center',
          behavior: "smooth"
        });
        setTimeout(() => e.target.reportValidity(), 50);  
        setTimeout(() => $form.find('input, select').off('focus.showValidationErrors'), 100);
    });
}
Chris Happy
  • 7,088
  • 2
  • 22
  • 49