Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Bootstrap 3 javascript checkbox and radio button toggle prevents angular's ng-model from updating on input #4516

Closed
pholly opened this issue Oct 18, 2013 · 21 comments · Fixed by #14685

Comments

@pholly
Copy link
Contributor

pholly commented Oct 18, 2013

When using Bootstrap 3's javascript buttons for checkboxes, the checkbox's ng-model does not get updated. Looking into the source code for both I know why.

Bootstrap 3 html for checkbox button group:

<div class="btn-group" data-toggle="buttons">
  <label class="btn btn-primary">
    <input type="checkbox"> Option 1
  </label>
  <label class="btn btn-primary">
    <input type="checkbox"> Option 2
  </label>
  <label class="btn btn-primary">
    <input type="checkbox"> Option 3
  </label>
</div>

Bootstrap 3 javascript for button toggle:

Button.prototype.toggle = function () {
        var $parent = this.$element.closest('[data-toggle="buttons"]')

        if ($parent.length) {
            var $input = this.$element.find('input')
              .prop('checked', !this.$element.hasClass('active'))
              .trigger('change')
            if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active')
        }

        this.$element.toggleClass('active')
    }

Angular source for checkbox (radio button is similar):

function checkboxInputType(scope, element, attr, ctrl) {
        var trueValue = attr.ngTrueValue,
            falseValue = attr.ngFalseValue;

        if (!isString(trueValue)) trueValue = true;
        if (!isString(falseValue)) falseValue = false;

        element.on('click', function () {
            scope.$apply(function () {
                ctrl.$setViewValue(element[0].checked);
            });
        });

        ctrl.$render = function () {
            element[0].checked = ctrl.$viewValue;
        };

        ctrl.$formatters.push(function (value) {
            return value === trueValue;
        });

        ctrl.$parsers.push(function (value) {
            return value ? trueValue : falseValue;
        });
    }

The problem is Angular's checkbox is listening for the click event whereas Bootstrap's button handles the click event and fires the change event on the checkbox input.

I wrote a simple directive that does exactly what Angular's checkbox click event listener does but instead listens for the change event.

appModule.directive('mccCheckboxDetectChange', [function mccCheckboxDetectChange() {

    return {
        replace: false,
        require: 'ngModel',
        scope: false,
        link: function (scope, element, attrs, ngModelCtrl) {
            element.on('change', function () {
                scope.$apply(function () {
                    ngModelCtrl.$setViewValue(element[0].checked);
                });
            });
        }
    };
}]);

Is there a reason why Angular listens for the click event for radio and checkboxes instead of the change event? Should I create a pull request and change the click event listener to be a change event listener? This would be my first pull request.

Thanks!
Philip

@stevschmid
Copy link

I ran into the same issue.

@caitp
Copy link
Contributor

caitp commented Jan 22, 2014

Could you guys post a really minimal reproduction? If it's something like pointer-events: none or something then I'm not sure what we can really do about that

@stevschmid
Copy link

fiddle

@caitp
Copy link
Contributor

caitp commented Jan 24, 2014

I see what's happening. So, I'm not sure if it would be right to handle this in angular core, this seems like it's more the domain of custom directives for interop with bootstrap. So writing your own is one thing, or otherwise hacking on n angular-strap or angular-ui/bootstrap or others.

But I'll see what people think about it

@stevschmid
Copy link

Why is angular listening for click events instead of change events for both radio buttons and check boxes?

@danse
Copy link

danse commented Feb 24, 2014

👍

3 similar comments
@ericpeters0n
Copy link

+1

@david-hollifield
Copy link

+1

@bimusiek
Copy link

👍

@david-hollifield
Copy link

To anyone that's interested, pholly's directive fixed the issue I was having with my radio buttons (with a minor tweak).

        return {
            replace: false,
            require: 'ngModel',
            scope: false,
            link: function (scope, element, attrs, ngModelCtrl) {
                element.on('change', function () {
                    scope.$apply(function () {
                        ngModelCtrl.$setViewValue(element[0].type.toLowerCase() == 'radio' ? element[0].value : element[0].checked);
                    });
                });
            }
        };

@fgmacedo
Copy link

+1

@Narretz Narretz added this to the Backlog milestone Jul 1, 2014
@sabudaye
Copy link

+1

1 similar comment
@fguillotpro
Copy link

+1

@Ericma1988
Copy link

@nikeee
Copy link

nikeee commented Jul 27, 2014

+1

@glorat
Copy link

glorat commented Oct 9, 2014

+1 and much gratitude for people here posting the workaround

@claudijo
Copy link

Thanks for posting a workaround for checkboxed toggle buttons that works reasonably well. However, as one of the referenced issues suggests, it seems like the workaround as mentioned by pholly, will require two clicks for the initial toggle if the checkbox is checked by default. See http://jsfiddle.net/6admtjf1/1/ (I forked stevschmid's fiddle, added ng-checked="true" to the checkbox, and removed bootstrap css to emphasize what is going on "behind the curtain").

Any ideas how to work around the workaround, to make this work as expected even if the default state for the button is checked?

@ppryde
Copy link

ppryde commented Feb 16, 2015

+1

@heridev
Copy link

heridev commented Oct 14, 2015

In order to support default checked checkboxes I ended up using


   .directive('checkboxWithChangeHandler', ['$timeout', function($timeout) {
     return {
       replace: false,
       require: 'ngModel',
       scope: false,
       link: function (scope, element, attr, ngModelCtrl) {
        $timeout( function(){
          if(element[0].checked) {
            $(element).parent().addClass('active')
          }
        }, 0);

         $(element).change(function () {
           scope.$apply(function () {
             ngModelCtrl.$setViewValue(element[0].checked);
           });
         });
       }
     };
   }]);

Is there a better way to support pre-selected checkboxes?

@ERobishaw
Copy link

Just use the angular-ui button function:
http://angular-ui.github.io/bootstrap/versioned-docs/0.12.0/#/buttons

@Fleisch
Copy link

Fleisch commented Dec 15, 2015

+1 .... @ERobishaw thank you.. instantly worked

Narretz added a commit to Narretz/angular.js that referenced this issue May 27, 2016
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes angular#4516
Closes angular#14667

BREAKING CHANGE:

input[radio] and input[checkbox] now need to be attached to the document to propagate events
correctly. This should only be of concern in unit-tests that compile input elements and
trigger click events on them. This is because we now listen to the change event which
gets automatically triggered by browsers when a checkbox or radio is clicked. However,
this may fail in some browsers when the elements are not attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      browserTrigger(inputElm[0], 'click');
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
$rootElement, and the $rootElement to the $document:

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      browserTrigger(inputElm[0], 'click');
      expect($rootScope.checkbox).toBe(true);
    });
```
Narretz added a commit to Narretz/angular.js that referenced this issue May 27, 2016
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes angular#4516
Closes angular#14667

BREAKING CHANGE:

input[radio] and input[checkbox] now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click" happens.

Two scenarios might need migration:

- Triggering click events:

Conventional trigger functions:

The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser and the trigger method, the change event
will not be fired when the input isn't attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
$rootElement, and the $rootElement to the $document:

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

triggerHandler():

If you are using this jQuery / jqLite function on the input elements, you have don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.

- Custom click events:

Before this change, custom click handlers on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault() and
event.stopImmediatePropagation() to prevent the input from updating.
If an app relied on the model being updated when the custom click handler has been called, you should
register a "change" handler instead.
Narretz added a commit to Narretz/angular.js that referenced this issue Mar 29, 2017
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes angular#4516
Closes angular#14667

BREAKING CHANGE:

input[radio] and input[checkbox] now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click" happens.

Two scenarios might need migration:

- Triggering click events:

Conventional trigger functions:

The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser and the trigger method, the change event
will not be fired when the input isn't attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
$rootElement, and the $rootElement to the $document:

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

triggerHandler():

If you are using this jQuery / jqLite function on the input elements, you have don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.

- Custom click events:

Before this change, custom click handlers on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault() and
event.stopImmediatePropagation() to prevent the input from updating.
If an app relied on the model being updated when the custom click handler has been called, you should
register a "change" handler instead.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 6, 2017
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes angular#4516
Closes angular#14667

BREAKING CHANGE:

input[radio] and input[checkbox] now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click" happens.

Two scenarios might need migration:

- Custom click events:

Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault()
to prevent the input from updating.

If an app uses a click event listener that expects ngModel to be updated when it is called, it now
needs to register a change event listener instead.

- Triggering click events:

Conventional trigger functions:

The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser and the trigger method, the change event
will not be fired when the input isn't attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
$rootElement, and the $rootElement to the $document:

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

triggerHandler():

If you are using this jQuery / jqLite function on the input elements, you have don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 6, 2017
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes angular#4516
Closes angular#14667

BREAKING CHANGE:

input[radio] and input[checkbox] now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
happens.

Two scenarios might need migration:

- Custom click events:

Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault()
to prevent the input from updating.

If an app uses a click event listener that expects ngModel to be updated when it is called, it now
needs to register a change event listener instead.

- Triggering click events:

Conventional trigger functions:

The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser and the trigger method, the change event
will not be fired when the input isn't attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
$rootElement, and the $rootElement to the $document:

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

triggerHandler():

If you are using this jQuery / jqLite function on the input elements, you have don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 6, 2017
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes angular#4516
Closes angular#14667

BREAKING CHANGE:

input[radio] and input[checkbox] now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
happens.

Two scenarios might need migration:

- Custom click events:

Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault()
to prevent the input from updating.

If an app uses a click event listener that expects ngModel to be updated when it is called, it now
needs to register a change event listener instead.

- Triggering click events:

Conventional trigger functions:

The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method,
the change event will not be fired when the input isn't attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
$rootElement, and the $rootElement to the $document.

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

triggerHandler():

If you are using this jQuery / jqLite function on the input elements, you have don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 11, 2017
…d of "click"

input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes angular#4516
Closes angular#14667

BREAKING CHANGE:

`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
happens.

Two scenarios might need migration:

- Custom click events:

Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault()
to prevent the input from updating.

If an app uses a click event listener that expects ngModel to be updated when it is called, it now
needs to register a change event listener instead.

- Triggering click events:

Conventional trigger functions:

The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method,
the change event will not be fired when the input isn't attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
`$rootElement`, and the `$rootElement` to the `$document`.

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

`triggerHandler()`:

If you are using this jQuery / jqLite function on the input elements, you don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
Narretz added a commit that referenced this issue Oct 13, 2017
…ngModels

input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.

It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html

Closes #4516
Closes #14667
Closes #14685

BREAKING CHANGE:

`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
happens.

Two scenarios might need migration:

- Custom click events:

Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault()
to prevent the input from updating.

If an app uses a click event listener that expects ngModel to be updated when it is called, it now
needs to register a change event listener instead.

- Triggering click events:

Conventional trigger functions:

The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method,
the change event will not be fired when the input isn't attached to the document.

Before:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
`$rootElement`, and the `$rootElement` to the `$document`.

After:

```js
    it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      $rootElement.append(inputElm);
      $document.append($rootElement);

      inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
      expect($rootScope.checkbox).toBe(true);
    });
```

`triggerHandler()`:

If you are using this jQuery / jqLite function on the input elements, you don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.