mdbootstrap / mdb-ui-kit

Bootstrap 5 & Material Design UI KIT
https://mdbootstrap.com/docs/standard/
Other
24.2k stars 3.53k forks source link

Floating labels for Password inputs not triggering on auto-fill #559

Closed lamoni closed 9 years ago

lamoni commented 9 years ago

When using the following input code, floating labels aren't triggering on auto-fill for the Password inputs (they're working fine for text inputs):

<div class="row">
    <div class="col-md-8 col-md-offset-2">
        <div class="form-group">
            <input type="email" class="form-control floating-label input-lg" name="email" placeholder="Username or Email" value="{{ old('email') }}">
        </div>
    </div>
</div>
<div class="row">
    <div class="col-md-8 col-md-offset-2">
        <div class="form-group">
            <input type="password" class="form-control floating-label input-lg" name="password" placeholder="Password" value="">
        </div>
    </div>
</div>

Screenshot: capture

FezVrasta commented 9 years ago

Have you enabled the autofill option?

lamoni commented 9 years ago

@FezVrasta How can I try that?

FezVrasta commented 9 years ago

$.material.options.autofill = true

Before you run the init script

lamoni commented 9 years ago

@FezVrasta still the same result

FezVrasta commented 9 years ago

It's weird, I never had problems with autofill enabled... Maybe you could provide a live demo on codpen or similar.

lamoni commented 9 years ago

Yeah it's odd. The floating-label "activates" correctly if I click anywhere on the screen, but not until then. The floating-label and activation of it work just fine on text inputs

PratibhaP commented 9 years ago

$.material.options.autofill = true; $.material.init(); using this floating label overlap problem sloves.

FezVrasta commented 9 years ago

@PratibhaP this is exactly what I have suggested to @lamoni, please @lamoni may you doublecheck?

PratibhaP commented 9 years ago

Yeah, Its working properly, @lamoni .

lamoni commented 9 years ago

Using the same html as listed in my original comment, and using the following, it is NOT working.

<script>
    $(document).ready(function() {
        $.material.options.autofill = true;
        $.material.init();
    });
</script>

[IMAGE REDACTED FOR PRIVACY REASONS - PLEASE SEE BELOW COMMENT FOR VIDEO CAPTURE OF THE ISSUE]

lamoni commented 9 years ago

I should note that this is in Chrome, where its auto-fill behavior puts the credentials into the text-box automatically (on Safari, you have to select the credentials from the dropbox, which indirectly fires the events, and it works as expected). This seems to be a problem with Bootstrap Material not detecting that there is input in the password box.

lamoni commented 9 years ago

Also, if you're testing this in something like CodePen, you are polluting the test since, assumedly, events are being fired when it compiles / is drawn. This issue happens when the following requirements are met:

  1. Google Chrome has saved credentials to use for auto-filling the text and password boxes
  2. No events have been triggered (in other words, this issue is apparent until you do anything on the page, whether it be clicking somewhere, or changing focus, etc... if you refresh the page, or end up clicking a link to the page and don't do anything, then the auto-fill floating-label behavior does NOT work).
lamoni commented 9 years ago

I've decided to grab a screen cap of this in action.

[REMOVED VIDEO LINK FOR PRIVACY REASONS]

FezVrasta commented 9 years ago

Every test I did ended up correctly, if you can provide a working demo I can study it.

PratibhaP commented 9 years ago

I have tested autofill of password in chrome and firefox also. Its working.

lamoni commented 9 years ago

@PratibhaP The screenshot and video screen capture say otherwise, but I digress.

@FezVrasta If this helps at all, here is the full form (maybe it's the existence of a checkbox or something causing this to break, and thus your tests aren't failing).

<form class="form" role="form" method="POST" action="{{ url('/auth/login') }}">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="form-group">
                <input type="email" class="form-control floating-label" placeholder="Email" name="email" value="{{ old('email') }}">
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="form-group">
                <input type="password" class="form-control floating-label input-lg" name="password" placeholder="Password" value="">
            </div>
        </div>
    </div>
    <div class="row">
        <div class="form-group">
            <div class="col-md-8 col-md-offset-2 text-right">
                <button type="submit" class="btn btn-primary btn-lg">Login</button>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="form-group">
            <div class="col-md-6 col-md-offset-2 text-left">
                <div class="checkbox">
                    <label>
                        <input type="checkbox" name="remember"> Remember Me
                    </label>
                </div>
            </div>
            <div class="col-md-2 text-right">
                <a class="btn btn-sm text-danger" href="{{ url('/password/email') }}" >Forgot</a>
            </div>
        </div>
    </div>
</form>

And the javascript:

    <script>
        $(document).ready(function() {
            $.material.options.autofill = true;
            $.material.init();
        });
    </script>
PratibhaP commented 9 years ago

screenshot from 2015-04-28 17 42 35

this is my firefox browser screenshot. Its working properly.

lamoni commented 9 years ago

@PratibhaP I'm not doubting it's working correctly for you. I'm doubting it's working correctly in every situation, which is obvious from my video capture. Also, your screencap shows the cursor in the text box, meaning you activated an event, meaning of course it will show the floating-label where it needs to be. My whole premise is based on the fact that if you actually have the credentials saved, refresh the page and DON'T touch anything, then the auto-fill doesn't activate properly.

PratibhaP commented 9 years ago

you are using "input-lg" class. what property it has?

FezVrasta commented 9 years ago

@lamoni I've not time to prepare a working demo, if you can provide me one (codepen, live website etc) then I can investigate

PratibhaP commented 9 years ago

@lamoni : without activating input event, still its coming. I have tested it on firefox and chrome. Don't know about safari and opera.

lamoni commented 9 years ago

I should note that I've tried this in Firefox and it works fine (Safari is a moot point since they don't auto-fill, apparently. You have to actually select your credentials from a drop-down box, which will, of course, fire an event), it seems to just be Google Chrome. @PratibhaP I've tried removing it, and it made no difference in the behavior.

@FezVrasta [REMOVED COMMENT TEXT FOR PRIVACY REASONS]

PratibhaP commented 9 years ago

In dom "empty" class attached to password field thats why its not working. In dom removed empty class from input, then its working.

lamoni commented 9 years ago

@PratibhaP Thanks for quick feedback. Is there anything I should be doing to avoid that, or does this need to be handled/fixed within bootstrap-material?

FezVrasta commented 9 years ago

the empty class is added by BS Material when it detects the field is empty, it should just remove it once autofill adds some value to it...

PratibhaP commented 9 years ago

But In my case empty class is not added to password field or in any input field.

FezVrasta commented 9 years ago

Probably there's something different in this case. Maybe Angular is doing something weird. I have to check once @lamoni enables my beta account.

PratibhaP commented 9 years ago

@lamoni : You have to remove that empty class for resolving this issue. @FezVrasta : how do we resloved it, because I checked it, The field is not empty. There is value in password field.

PratibhaP commented 9 years ago

@FezVrasta : I am AngularJS framework but its working properly. No issues with angular, i think so.

lamoni commented 9 years ago

@FezVrasta It's actually Laravel's Blade templating engine, but that page is doing very minimal Blade (only filling the values for the text areas), and I think that it is irrelevant since it doesn't touch anything client-side. Blade is only "active" on the server where it generates the HTML.

Example of the actual source code:

<form class="form" role="form" method="POST" action="{{ url('/auth/login') }}">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="form-group">
                <input type="email" class="form-control floating-label" placeholder="Email" name="email" value="{{ old('email') }}">
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="form-group">
                <input type="password" class="form-control floating-label" name="password" placeholder="Password" value="">
            </div>
        </div>
    </div>
    <div class="row">
        <div class="form-group">
            <div class="col-md-8 col-md-offset-2 text-right">
                <button type="submit" class="btn btn-primary btn-lg">Login</button>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="form-group">
            <div class="col-md-6 col-md-offset-2 text-left">
                <div class="checkbox">
                    <label>
                        <input type="checkbox" name="remember"> Remember Me
                    </label>
                </div>
            </div>
            <div class="col-md-2 text-right">
                <a class="btn btn-sm text-danger" href="{{ url('/password/email') }}" >Forgot</a>
            </div>
        </div>
    </div>
</form>
FezVrasta commented 9 years ago

Writing $.material.options.autofill it returns false.

Seems like you have not that option enabled, maybe you are enabling it in some different page..

lamoni commented 9 years ago

@FezVrasta if you check the source of that login page, I have:

    <script>
        $(document).ready(function() {
            $.material.options.autofill = true;
            $.material.init();
        });
    </script>

Can you confirm that is the correct way to set that?

PratibhaP commented 9 years ago

Its correct way to enable to autofill.

lamoni commented 9 years ago

It appears to be an issue with the way Google Chrome handles auto-filling password fields and how bootstrap-material relies on checking for an empty field to remove the empty class.

If you refresh the login page, and then on console run: console.log(document.getElementsByName("password")[0].value)

You'll see there is no value (which the autofill section of bootstrap-material relies on checking... it is empty, despite Google Chrome showing the * characters in the text box). Once you fire an event, run that console.log command again and you'll see it has a value. So it looks like Chrome doesn't actually fill the value in the DOM until an event has been fired, thus bootstrap-material won't remove the empty class

FezVrasta commented 9 years ago

No, the problem is that you have not enabled $.material.options.autofill = true;.

I've added a breakpoint before the document.ready event and enabled it manually and this is the result:

image

lamoni commented 9 years ago

@FezVrasta Did you look at the source code?! I am doing exactly that.

lamoni commented 9 years ago

@FezVrasta Even changing autofill to "true" in materials.js doesn't fix it. Unbelievable that this is going to be closed despite being an obvious bug.

lamoni commented 9 years ago

After setting "autofill" to true in material.js AND confirming that it is true from console after doing so: screen shot 2015-04-28 at 9 16 30 am

FezVrasta commented 9 years ago

I don't know what are you doing wrong in your source code. I've just enabled the correct option of my framework and everything works correctly. I'm not paid to fix problems with the source code of other peoples (well, I'm not paid at all...)

I would doublecheck your source code to understand why it happens.

lamoni commented 9 years ago

I'm not expecting you to fix this alone, I've been more than willing to dedicate time to helping because I like your framework, but to close an issue despite obvious evidence that there is an issue is just scary. It's not a "problem with the source code of others".

PratibhaP commented 9 years ago

@lamoni : remove that value="" from password field and try it again.

lamoni commented 9 years ago

@PratibhaP Removed the value="" for the password input, but still seeing same results.

FezVrasta commented 9 years ago

I could even keep it open, but I fixed it locally editing your code so I wonder why I should..

PratibhaP commented 9 years ago

@lamoni then i have to take a look into it.

lamoni commented 9 years ago

@FezVrasta I've even changed autofill to true in material.js. Take a look at the page again and see for yourself that despite being hard-coded to true in your code, that it does NOT work.

lamoni commented 9 years ago

I put this together for you to test on: [REDACTED]

I have stripped out ANYTHING that is not bootstrap-material-design related, to further prove my point that it's not "the source code of others" causing this issue.

The only included Javascript libraries are the boostrap-material-design dependencies (jquery 2.1.3, bootstrap 3.3.1, ripples.js, and material.js). The only other javascript code is the $(document).ready() which is assigning autofill to true AND init()'ing material. I believe this is probably an issue with the following code due to Chrome not actually filling in the password box (or firing an event) until an event has been fired for a different element (until then, it "fakes" the asterisks):

      .on("keyup change", ".form-control", function() {
        var $this = $(this);
        if ($this.val() === "" && (typeof $this[0].checkValidity != "undefined" && $this[0].checkValidity())) {
          $this.addClass("empty");
        } else {
          $this.removeClass("empty");
        }

To further stomp this into the ground, here's a Google Chrome bug report stating this very behavior:

https://code.google.com/p/chromium/issues/detail?id=352527

danbars commented 9 years ago

I am experiencing the same issue. After a lot of reading and research, I've found the problem, and a fix!

The problem happens because password fields in chrome are not really auto-filled until the user interacts with the page, in order to avoid security risks in password harvesting. To make it harder, it is auto filled if you hit F5 (because it means the user is on that page), but not if the page was loaded in any other way (programatically or by typing the URL).

This is why `$.material.options.autofill' does not do anything - because you can't identify this is the case,

Luckily, after a lot of pressure, someone in the chrome team added a css selector to identify auto-filled fields in chrome.

So the fix is quite simple, just add a special handling for chrome in the material css

.form-control-wrapper .form-control:focus ~ .floating-label, 
.form-control-wrapper .form-control:not(.empty) ~ .floating-label, 
.form-control-wrapper .form-control:-webkit-autofill ~ .floating-label {
  top: -10px;
  font-size: 10px;
  opacity: 1;
}
FezVrasta commented 9 years ago

A PR would be very welcome

rosskevin commented 9 years ago

Resolved by #725 and released as 0.4.0

paulirwin commented 9 years ago

This issue is not resolved in 0.4.0. It needs the -webkit-autofill selector change.

image