Как исправить проблему выбора IE при динамическом изменении параметров
У меня есть набор выбирает, что все имеют те же возможности. Затем я запускаю эти параметры через фильтр, чтобы любые параметры, выбранные в другом выборе, не отображались в выборе. Смотрите этот jsFiddle (в браузере без IE), чтобы увидеть, что я имею в виду. В основном я предотвращаю один и тот же вариант от выбора несколько раз среди выбирает
теперь то, что я сделал, имеет проблему в IE. Откройте эту скрипку в IE (я только пробовал ее в IE9, но я предполагаю предыдущие версии имеют ту же проблему). Измените последний выбор на AAA. Обратите внимание, как 3 других выбора все изменили то, что они отображают. Модель для них не менялась, но т. е. как-то задыхается при изменении параметров.
мои вопросы-во-первых, я делаю что-то неправильно с этой функцией вообще? Этот же код делает именно то, что я хочу в 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); }); } } } ]);
Я наконец придумал решение, которое работает для моих потребностей. В основном то, что происходит, заключается в том, что текст для опции в выбранном индексе указывает на старую строку, которая раньше была в этом месте. Я считаю, что изменение этого текста обновляет строки и / или ссылки. Я сделал что-то вроде этого:
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, если label пуст в 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
до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>
Я нашел ту же ошибку в IE с select's.. эта ошибка является результатом клонирования узлов DOM..
Если вы создаете экземпляр SELECT like (jQuery style):
$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 или на a приличный браузер.Вы были предупреждены..!!
Кажется, что 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++; }); } }
на линии Мэттью Берг ответ, я изменил его для работы с помощью директивы 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;
У меня есть временное решение проблемы, т. е. выбора
перед исправлением: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 и удалить его. дайте мне знать, что это работает для вас