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