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

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

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

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

Любая помощь будет высоко оценена. Спасибо.

- ОБНОВЛЕНИЕ -

Это исправлено в Angular самостоятельно с версия 1.3.3, используя A. S. Ranjan ниже. См. Новую скрипту с 1.3.3: http://jsfiddle.net/m2ytyapv/

//dummy code so I can post the edit

Ответ 1

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

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

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

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

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

Ответ 2

Я столкнулся с той же проблемой в ту ночь и, бросив все, что мог придумать, я пришел к выводу, что 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. Единственное, что наверняка зафиксировало это, было частью кода.

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

Проблема, похоже, связана с порядком опций, возвращаемых фильтром. При изменении последней опции на 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/ (старый)

Update:

Я сделал некоторую отладку и нашел строку, которая вызывает проблемы в 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, чтобы выбрать элементы внутри ng-repeat:

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

Ответ 6

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

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

$select = $template.clone();

а затем выполните:

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

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

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

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

ошибок не было:)

Ответ 7

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

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

Я фактически использовал 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 (а не MSIE или Microsoft Internet Explorer)... поэтому будьте осторожны, когда вы тестируете, работает ли ваш код в IE, или в приличном браузере.

Вы были предупреждены..!!

Ответ 8

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

Ответ 9

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

Ответ 10

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

Ответ 11

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

Ответ 12

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

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

Ответ 13

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