How to fix IE select issue when dynamically changing options

前端 未结 13 518
南旧
南旧 2020-12-04 16:38

I have a set of selects that all have the same options. Then I run those options through a filter so that any options that are selected in a different select don\'t show up

相关标签:
13条回答
  • 2020-12-04 16:49

    The problem seems to be related to the order of the options returned by the filter. When you change the last option to A, the other select options changes. What seems to cause a problem for IE is that the selected option changes place. In the first select box C is selected from the options in the following order: A, B, C, D. The selected option is the third option. When you change the forth select box from G to A, the filter changes the options in the first box to B, C, D, G. The selected option is now the second option, and this causes a problem with IE. This might be a bug in Angular, or it might be to some strange behavior in IE. I've created a fork that works around this by making sure that selected element is always the first option from the filtered options:

       var newOptions = [],selected;
        angular.forEach(allOptions, function (currentOption) {
            if (!isIdInUse(selectedIds, currentOption.id)){
                newOptions.push(currentOption);
            }else if(currentOption.id == selectedIds[parseInt(index)]){
                selected = currentOption;
            }
        });
        if(selected){newOptions.unshift(selected);}
    

    http://jsfiddle.net/XhxSD/ (old)

    Update:

    I did some debugging and found the line that causes problems in IE, but I don't understand why. It does seem like a rendering bug or something. I've created another workaround that doesn't require any rearranging of the options - it's a directive that watches for changes on the select element. If a change is detected it appends an option and removes it immediately:

    .directive('ieSelectFix',function($timeout){
      return {
        require:'select',
        link: function (scope, element) {
          var isIE = document.attachEvent;
    
          if(isIE){
            $timeout(function(){
              var index = element.prop('selectedIndex'), children = element.children().length;
              scope.$watch(function(){
                if(index !== element.prop('selectedIndex') || children !== element.children().length){
                  index = element.prop('selectedIndex');
                  children = element.children().length;
                  var tmp =angular.element('<option></option>');
                  element.append(tmp);
                  tmp.remove();
                }
              })
    
            });
          }
        }
      }
    });
    

    Just add ie-select-fix to select elements inside ng-repeats:

    <div ng-app="myApp" ng-controller="MyCtrl">
      <select ie-select-fix ng-repeat="currId in selectedIds" ng-model="selectedIds[$index]"  ng-options="currOption.id as currOption.value for currOption in myObj | myfilter:selectedIds:$index"></select><br>
      {{selectedIds}}
    </div>
    

    http://jsfiddle.net/VgpyZ/ (new)

    0 讨论(0)
  • 2020-12-04 16:52

    I have the fix.

    We have to add and remove options list to trigger the rendering in IE8.

    http://kkurni.blogspot.com.au/2013/10/angularjs-ng-option-with-ie8.html


    /**
     * Fix for IE select menus getting stuck when their underlying list changes.
     * Original code: http://kkurni.blogspot.com.au/2013/10/angularjs-ng-option-with-ie8.html
     * 
     * Set the `ie-select-fix` attribute to the model expression that should trigger the list to re-render.
     * 
     * @example <select ng-model="modelValue" ie-select-fix="itemList" ng-options="item.label for item in itemList">
     */
    app.directive('ieSelectFix', ['$document',
            function($document) {
    
                return {
                    restrict: 'A',
                    require: 'ngModel',
                    link: function(scope, element, attributes, ngModelCtrl) {
                        var isIE = $document[0] && $document[0].attachEvent;
                        if (!isIE) return;
    
                        var control = element[0];
                        //to fix IE8 issue with parent and detail controller, we need to depend on the parent controller
                        scope.$watch(attributes.ieSelectFix, function() {
                            // setTimeout is needed starting from angular 1.3+
                            setTimeout(function() {
                                //this will add and remove the options to trigger the rendering in IE8
                                var option = document.createElement("option");
                                control.add(option,null);
                                control.remove(control.options.length-1);
                            }, 0);
                        });
                    }
                }
            }
        ]);
    
    0 讨论(0)
  • 2020-12-04 16:56

    The rendering gets updated and synched if you change some attribute. An innocuous change may be to set the selectedIndex attribute to its own value:

    function fixIEselect() {
        for (var nForm = 0; nForm < document.forms.length; ++nForm) {
            var form = document.forms[nForm];
            var children = form.children;
            for (var nChild = 0; nChild < children.length; ++nChild) {
                var child = children.item(nChild);
                if (child.tagName == "SELECT") {
                    alert("Fixed: " + child.name);
                    child.selectedIndex = child.selectedIndex; // dummy nop but not
                }
            }
        }
    }
    
    fixIEselect();
    
    0 讨论(0)
  • 2020-12-04 17:02

    I had to change scope.$watch to scope.$watchCollection to make @kkurni solution above to work for IE9. Just wanted to help others out who were still having issues in IE9 for rendering select options when they change.

    0 讨论(0)
  • 2020-12-04 17:03

    I experienced the same issue the other night and after throwing everything I could think of at it I've come to the conclusion that IE just doesn't want to handle updating filters when using selects.

    My solution is to change your selects to look like this:

     <select class="selectList" ng-repeat="currId in selectedIds" ng-model="selectedIds[$index]"  ng-options="currOption.id as currOption.value for currOption in myObj | myfilter:selectedIds:$index" data-ng-change="fixIE()"></select>
    

    They now have a class and an ng-change on them. Then in your controller do this fun little bit of code:

    $scope.fixIE = function(){
        //code to check if IE so the other browsers don't get this ugly hack.
        var selectLists = document.querySelectorAll(".selectList");
        for(var x = 0;x  < selectLists.length; x++){
            selectLists[x].parentNode.insertBefore(selectLists[x], selectLists[x]);
        }       
    };
    

    What it does is rip the elements out of the DOM and replace them into the same location. Here's a working fiddle jsFiddle

    Some of the other solutions I tried that didn't involve javascript were things like toggling the display/visibility of the select. Having their zIndex's moved. The only thing that for sure fixed it was this piece of code.

    0 讨论(0)
  • 2020-12-04 17:03

    There is less expensive way to enforce control re-rendering after dynamic options are added. So, instead of inserting/removing dummy element to the dropdown you can reset CSS styles which cause control rendering, e.g.

    selElement.style.zoom = selElement.style.zoom ? "" : 1;
    
    0 讨论(0)
提交回复
热议问题