Как исправить IE выберите проблему при динамическом изменении параметров

У меня есть набор вариантов, которые имеют одинаковые параметры. Затем я запускаю эти параметры через фильтр, чтобы любые параметры, выбранные в другом select, не отображались в select. См.этот jsFiddle (в браузере без IE), чтобы увидеть, что я имею в виду. В основном я предотвращаю выбор одного и того же параметра несколько раз среди выбранных

теперь, то, что я сделал, имеет проблему в IE. Откройте эту скрипку в IE (я только пробовал ее в IE9, но я предполагаю предыдущие версии имеют ту же проблему). Измените последний выбор на AAA. Обратите внимание, как 3 других выбирает все, что они отображают. Модель для них не менялась, но IE как-то задыхается при изменении опций.

мои вопросы-во-первых, я делаю что-то неправильно с этой функцией вообще? Этот же код делает именно то, что я хочу в Chrome и FF, но я делаю то, что не должен? Во-вторых, как я могу обойти это в IE? Я попробовал тайм-ауты это бы прояснило и переустановило модель, но вещи заметные прыгали вокруг. Мне интересно,есть ли для этого хороший, чистый, низкий обходной путь.

любая помощь была бы очень признательна. Спасибо.

-- UPDATE--

Это основные в угловой себе с версия 1.3.3 используя решение А. С. Раньяна ниже. См. новую скрипку с 1.3.3: http://jsfiddle.net/m2ytyapv/

//dummy code so I can post the edit

13 ответов


Я испытал ту же проблему на днях, и после того, как бросил все, что я мог подумать об этом, я пришел к выводу, что IE просто не хочет обрабатывать фильтры обновления при использовании selects.

мое решение состоит в том, чтобы изменить ваш выбор, чтобы выглядеть так:

 <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-изменение на них. Затем в вашем контроллере сделайте этот забавный маленький кусочек кода:

$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 переехал. Единственное, что наверняка исправило это был этот кусок кода.


У меня есть исправление.

мы должны добавить и удалить список опций, чтобы вызвать рендеринг в 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);
                    });
                }
            }
        }
    ]);

Я наконец придумал решение, которое работает для моих потребностей. В основном происходит то, что текст для опции в выбранном индексе указывает на старую строку, которая раньше была в этом месте. Я считаю, что изменение этого текста обновляет строки и / или ссылки. Я сделал что-то вроде этого:--5-->

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

вот обновленная скрипка:http://jsfiddle.net/H48sP/35/

в моем приложении у меня есть директива, где эти выборы, и так я и делаю element.find("select") вместо $("select") чтобы ограничить область выбора элемента. Текст вынужден обновляться и поэтому отображается правильно после выполнения всех циклов дайджеста.

если вы столкнулись с этой же проблемой, вам может понадобиться, чтобы добавить $timeout как в скрипке, и / или вам может потребоваться позже удалить дополнительное пространство, которое было добавлено в текст опции, если это станет проблемой.


добавление пары строк следующие места (выделены жирным шрифтом как**) в функции рендеринга selectDirective в angular.js отлично работал для меня. Я ищу, есть ли какое-либо другое возможное решение, кроме исправления 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);**

проблема заключалась в том, что атрибут label HTMLOptionElement не совпадает с атрибутом text, если метка пуста в IE.

Это можно увидеть, добавив следующий код после загрузки экрана и глядя на веб-консоль FF и IE, чтобы увидеть разницу. Если вы раскомментируете последнюю строку, где метка установлена в текст, она отлично работает. Альтернативно патч угловой.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;
});

проблема, похоже, связана с порядком параметров, возвращаемых фильтром. При изменении последнего параметра на A, другие изменения параметров выбора. Что, по-видимому, вызывает проблему для IE, так это то, что выбранный параметр меняет место. В первом поле выберите C выбирается из параметров в следующем порядке:A, B, C, D. Выбрана опция третий вариант. При изменении четвертого поля выберите из G to 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, но я не понимаю, почему. Это действительно похоже на ошибку рендеринга или что-то в этом роде. Я создал еще один обходной путь, который не требует перестановки параметров - это директива, которая следит за изменениями в элементе select. Если изменение обнаружено, оно добавляет параметр и немедленно удаляет его:

.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 для выбора элементов внутри НГ-повторяет:

<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/ (новый)


Я нашел ту же ошибку в IE с select's.. эта ошибка является результатом клонирования узлов DOM..

Если вы создаете экземпляр SELECT like (стиль jQuery):

$select = $template.clone();

а потом делаем:

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

вы получите ошибку, описанную выше..

но, если вы создаете экземпляр

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

нет ошибки :)


О, я отправляюсь в ад за следующим советом...!

Я пробовал эти предложения, но ни один не работал для меня.

Я фактически использовал Angular для заполнения элементов управления select несколькими параметрами в каждом.

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

иногда Angular заполняет эти элементы управления, появляются новые данные, но в IE вы не можете прокручивать вверх и вниз, чтобы просмотреть все параметры.

но, если вы нажмете F12, измените ширину и верните ее на свою исходная ширина, затем IE снова вернется к жизни, и вы можете прокручивать вверх и вниз в списке значений.

Итак, моим решением было назвать это, через секунду или около того после того, как угловой закончил заполнять элементы управления:

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

(Я говорил вам, что это было хитрое решение..)

еще одна вещь: помните, что в IE11, navigator.appName сейчас вернется NETSCAPE (а не MSIE или Microsoft Internet Explorer)... поэтому будьте осторожны при тестировании, если ваш код работает на IE или на приличный браузер.

вас предупредили..!!


Кажется, что 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/


на линии Мэттью Берг ответ, я изменил его для работы с помощью директивы 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;
}

Мне пришлось изменить область видимости.$ смотреть в прицел.$ watchCollection, чтобы сделать решение @kkurni выше для работы с IE9. Просто хотел помочь другим, у которых все еще были проблемы в IE9 для рендеринга параметров выбора при их изменении.


при изменении какого-либо атрибута рендеринг обновляется и синхронизируется. Безобидным изменением может быть установка 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();

существует менее дорогой способ принудительного повторного рендеринга управления после добавления динамических параметров. Таким образом, вместо вставки/удаления фиктивного элемента в раскрывающемся списке вы можете сбросить стили CSS, которые вызывают рендеринг управления, например

selElement.style.zoom = selElement.style.zoom ? "" : 1;

У меня есть обходной путь для проблемы раскрывающегося списка 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, чтобы перезапустить select и удалить его. дайте мне знать, что это работает для вас