64

我有一组选择,它们都有相同的选项。然后我通过过滤器运行这些选项,以便在不同选择中选择的任何选项都不会显示在选择中。请参阅此 jsFiddle(在非 IE 浏览器中)以了解我的意思。基本上我阻止在选择中多次选择相同的选项

现在,我所做的在 IE 中存在问题。在 IE 中打开那个小提琴(我只在 IE9 中尝试过,但我猜以前的版本也有同样的问题)。将最后一个选择更改为 AAA。请注意其他 3 个选择如何全部更改了它们显示的内容。它们的模型没有改变,而是在选项改变时,即某种方式窒息。

我的问题是首先,我在这个功能上做错了吗?同样的代码在 Chrome 和 FF 中完全符合我的要求,但我是否在做一些我不应该做的事情?其次,我怎样才能在 IE 中解决这个问题?我尝试了一些可以清除并重新设置模型的超时,但明显的事情发生了变化。我想知道是否有一个好的、干净的、低影响的解决方法。

任何帮助将非常感激。谢谢。

- 更新 -

这已在 Angular 本身中使用AS Ranjan 的解决方案在1.3.3 版中修复。请参阅 1.3.3 的新小提琴:http: //jsfiddle.net/m2ytyapv/

//dummy code so I can post the edit
4

13 回答 13

29

前几天晚上我遇到了同样的问题,在抛出了我能想到的一切之后,我得出的结论是,IE 只是不想在使用选择时处理更新过滤器。

我的解决方案是将您的选择更改为如下所示:

 <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>

他们现在有一个班级和一个 ng-change 。然后在您的控制器中执行以下有趣的代码:

$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]);
    }       
};

它所做的是将元素从 DOM 中分离出来并将它们替换到相同的位置。这是一个工作小提琴jsFiddle

我尝试过的其他一些不涉及 javascript 的解决方案是切换选择的显示/可见性之类的。移动他们的 zIndex。唯一确定修复它的是这段代码。

于 2013-05-14T12:41:22.673 回答
20

我有解决办法。

我们必须添加和删除选项列表才能触发 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);
                    });
                }
            }
        }
    ]);
于 2013-10-30T01:59:22.053 回答
12

我终于想出了一个适合我需要的解决方案。基本上似乎正在发生的事情是所选索引处的选项文本指向曾经位于该位置的旧字符串。我相信更改此文本会更新字符串和/或引用。我做了这样的事情:

angular.forEach($("select"), function (currSelect) {
     currSelect.options[currSelect.selectedIndex].text += " ";
});

这是更新的小提琴:http: //jsfiddle.net/H48sP/35/

在我的应用程序中,我有一个指令,这些选择在哪里,所以我这样做element.find("select")而不是$("select")限制元素选择的范围。文本被强制刷新,因此在所有摘要循环运行后正确显示。

如果您遇到同样的问题,您可能需要$timeout在小提琴中添加一个like,和/或如果这成为问题,您可能需要稍后删除添加到选项文本的额外空间。

于 2013-05-14T18:25:35.117 回答
5

在 angular.js 中 selectDirective 的渲染函数中添加几行以下位置(以粗体标记为 **)对我来说效果很好。我正在寻找除了修补 angularJS 或下面给出的 forEach 之外是否还有其他可能的解决方案?

            if (existingOption.label !== option.label) {
              lastElement.text(existingOption.label = option.label);
              **lastElement.attr('label', existingOption.label);**
            }

              (element = optionTemplate.clone())
                  .val(option.id)
                  .attr('selected', option.selected)
                  .text(option.label);
              **element.attr('label', option.label);**

问题是如果标签在 IE 中为空,则 HTMLOptionElement 的标签属性与文本属性不同。

这可以通过在屏幕加载后添加以下代码并查看 FF 和 IE 的 Web 控制台来查看差异来验证。如果您取消注释标签设置为文本的最后一行,它可以正常工作。或者如上所述修补 angular.js。

// This is an IE fix for not updating the section of dropdowns which has ng-options with filters
angular.forEach($("select"), function (currSelect) {
    console.log("1.text ", currSelect.options[currSelect.selectedIndex].text);
    console.log("1.label ", currSelect.options[currSelect.selectedIndex].label);
    //console.log("1.innerHTML ", currSelect.options[currSelect.selectedIndex].innerHTML);
    //console.log("1.textContent ", currSelect.options[currSelect.selectedIndex].textContent);
    //console.log("1.cN.data ", currSelect.options[currSelect.selectedIndex].childNodes[0].data);
    //console.log("1.cN.nodeValue ", currSelect.options[currSelect.selectedIndex].childNodes[0].nodeValue);
    //console.log("1.cN.textContent ", currSelect.options[currSelect.selectedIndex].childNodes[0].textContent);
    //console.log("1.cN.wholeText ", currSelect.options[currSelect.selectedIndex].childNodes[0].wholeText);
    //console.log("1. ", currSelect.options[currSelect.selectedIndex], "\n");

    //currSelect.options[currSelect.selectedIndex].label = "xyz";
    //currSelect.options[currSelect.selectedIndex].label = currSelect.options[currSelect.selectedIndex].text;
});
于 2014-08-14T14:16:56.420 回答
3

问题似乎与过滤器返回的选项的顺序有关。当您将最后一个选项更改为 时A,其他选择选项会更改。似乎对 IE 造成问题的是所选选项更改了位置。在第一个选择框中C按以下顺序从选项中选择:A, B, C, D。选中的选项是第三个选项。当您将第四个选择框从更改G为 时A,过滤器会将第一个框中的选项更改为B, C, D, G。所选选项现在是第二个选项,这会导致 IE 出现问题。这可能是 Angular 中的错误,也可能是 IE 中的一些奇怪行为。通过确保所选元素始终是过滤选项中的第一个选项,我创建了一个解决此问题的分支:

   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/(旧)

更新:

我做了一些调试,发现在 IE 中导致问题的行,但我不明白为什么。它看起来确实像一个渲染错误或其他东西。我创建了另一个不需要重新排列选项的解决方法——它是一个监视选择元素变化的指令。如果检测到更改,它会附加一个选项并立即将其删除:

.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();
            }
          })

        });
      }
    }
  }
});

只需添加 ie-select-fix 来选择 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/(新)

于 2013-05-09T20:26:30.203 回答
3

我在 IE 中发现了与 select 相同的错误。这个错误是克隆 DOM 节点的结果。

如果你实例化一个 SELECT 像(jQuery 风格):

$select = $template.clone();

然后做:

$select.html('<option>111</option>');

你会得到上面描述的错误..

但是,如果你实例化

$select = $('< div />').html( $template ).html();

没有发生错误:)

于 2013-12-16T13:26:24.337 回答
2

哦,我要去地狱寻求以下建议......!

我尝试了这些建议,但没有一个对我有用。

我实际上是在使用 Angular 来填充选择控件,每个控件都有多个选项。

<select class="cssMultipleSelect" multiple="multiple" ...>

有时,Angular 会填充这些控件,新数据会出现,但在 IE 中,您无法上下滚动来查看所有选项。

但是,如果你按 F12,修改宽度,然后将其恢复到原来的宽度,那么 IE 会重新焕发活力,你可以在值列表中上下滚动。

所以,我的解决方案是在 Angular 完成填充控件后一秒钟左右调用它:

function RefreshMultipleSelectControls()
{
    //  A dodgy fix to an IE11 issue.
    setTimeout(function () {
        $(".cssMultipleSelect").width("");
    }, 1500);
    setTimeout(function () {
        $(".cssMultipleSelect").width(298);
    }, 1600);
}

(我告诉过你这是一个狡猾的修复..)

另一件事:请记住,在 IE11 中,navigator.appName现在将返回NETSCAPE(而不是MSIEor Microsoft Internet Explorer)......所以当您测试您的代码是在 IE 上还是在体面的浏览器上运行时要小心。

你被警告了..!!

于 2015-09-30T14:09:00.873 回答
0

似乎ie9的索引有问题。以第二个示例并将其更改为以下代码即可:

  var hwcalcModule = angular.module('ie9select', []);

  function AnimalCtrl($scope) {
    $scope.categories = [{
        name: "Cats",
        kinds: ["Lion", "Leopard", "Puma"]
    }, {
        name: "Dogs",
        kinds: ["Chihua-Hua", " Yorkshire Terrier", "Pitbull"]
    }];

  $scope.animals = [{
      category: $scope.categories[1],
      kind: $scope.categories[1].kinds[1]
  }];

  $scope.changeCategory = function (animal) {
      console.log(animal.category.name);
      var name = animal.category.name;
      var index = 0;
       angular.forEach($scope.categories, function (currentOption) {
           console.log(currentOption.name);
          if (name == currentOption.name)
          {
              console.log(index);
              $scope.animals = [{
                  category: $scope.categories[index],
                  kind: $scope.categories[index].kinds[0]
              }];
           }
           index++;
       });
  }
}

http://jsfiddle.net/seoservice/nFp62/10/

于 2013-02-25T17:20:57.780 回答
0

根据 Mathew Berg的回答,我将其修改为使用 AngularJS 指令:

angular.module('select',[]).directive("select", function() {
    return {
      restrict: "E",
      require: "?ngModel",
      scope: false,
      link: function (scope, element, attrs, ngModel) {

        if (!ngModel) {
          return;
        }

        element.bind("change", function() {
            //Fix for IE9 where it is not able to properly handle dropdown value change
            //The fix is to rip out the dropdown from DOM and add it back at the same location
            if (isIE9){
                this.parentNode.insertBefore(this, this);   //this rips the elements out of the DOM and replace it into the same location.
            }
        })
      }
   }
});

这样,修复适用于select项目中的所有元素,您不必更改任何现有的 HTML 标记。我还使用以下方法来检测 IE 版本以将isIE9变量设置为true

var Browser = {
    IsIE: function () {
        return navigator.appVersion.indexOf("MSIE") != -1;
    },
    Navigator: navigator.appVersion,
    Version: function() {
        var version = 999; // we assume a sane browser
        if (navigator.appVersion.indexOf("MSIE") != -1)
            // bah, IE again, lets downgrade version number
            version = parseFloat(navigator.appVersion.split("MSIE")[1]);
        return version;
    }
};

var oldIE = false;      //Global Variable
var isIE9 = false;      //Global Variable
if (Browser.IsIE && Browser.Version() <= 8) {
    oldIE = true;
}

if (Browser.IsIE && Browser.Version() == 9) {
    isIE9 = true;
}
于 2014-08-05T11:44:37.213 回答
0

我不得不将 scope.$watch 更改为 scope.$watchCollection 以使上述@kkurni 解决方案适用于 IE9。只是想帮助其他在 IE9 中仍然存在问题的人,以便在更改时呈现选择选项。

于 2014-08-18T23:37:39.907 回答
0

如果您更改某些属性,渲染会更新和同步。一个无害的变化可能是将selectedIndex属性设置为它自己的值:

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();
于 2015-01-13T20:02:59.443 回答
0

添加动态选项后,执行控制重新渲染的成本较低。因此,您可以重置导致控件呈现的 CSS 样式,而不是在下拉列表中插入/删除虚拟元素,例如

selElement.style.zoom = selElement.style.zoom ? "" : 1;
于 2015-02-15T22:19:31.990 回答
0

我有一个解决 IE 选项列表问题的方法

修复前:http ://plnkr.co/edit/NGwG1LUVk3ctGOsX15KI?p=preview

修复后:http ://plnkr.co/edit/a7CGJavo2m2Tc73VR28i?p=preview

$("select").click(function(){
  $(this).append('<option></option>');
   $(this).find('option:last').remove();

});

我刚刚为 dom 添加了虚拟选项以重新呈现选择并将其删除。让我知道它对你有用

于 2017-09-25T13:20:57.360 回答