web-dev-qa-db-fra.com

Comment réparer IE sélectionner le problème lors du changement dynamique d'options

J'ai un ensemble de sélections qui ont toutes les mêmes options. Ensuite, j'exécute ces options via un filtre afin que les options sélectionnées dans une sélection différente n'apparaissent pas dans la sélection. Voir this jsFiddle _ (dans un navigateur non-IE) pour voir ce que je veux dire. Fondamentalement, j'empêche la même option d'être sélectionnée plusieurs fois parmi les sélections.

Maintenant, ce que j'ai fait a un problème dans IE. Ouvrez ce violon dans IE (je ne l'ai essayé que dans IE9, mais je suppose que les versions précédentes ont le même problème). Changez la dernière sélection en AAA. Remarquez comment les 3 autres sélectionneurs ont tous changé ce qu’ils affichent. Le modèle pour eux n'a pas changé, mais IE s'étouffe quand les options sont modifiées.

Ma question est d'abord, est-ce que je fais quelque chose de mal avec cette fonctionnalité en général? Ce même code fait exactement ce que je veux dans Chrome et FF, mais est-ce que je fais quelque chose que je ne devrais tout simplement pas être? Deuxièmement, comment puis-je contourner cela dans IE? J'ai essayé des délais d'attente qui effaceraient et redéfiniraient le modèle, mais les choses remarquables ont sauté. Je me demande s’il existe une solution de contournement efficace, propre et à faible impact.

Toute aide serait très appréciée. Merci.

--METTRE À JOUR--

Cela a été corrigé dans Angular lui-même avec version 1.3.3 en utilisant solution de A. S. Ranjan ci-dessous. Voir le nouveau violon avec 1.3.3: http://jsfiddle.net/m2ytyapv/

//dummy code so I can post the edit
64
dnc253

J'ai enfin trouvé une solution qui répond à mes besoins. Fondamentalement, ce qui semble se produire est que le texte de l'option de l'index sélectionné pointe vers l'ancienne chaîne qui se trouvait auparavant à cet endroit. Je crois que changer ce texte met à jour les chaînes et/ou les références. J'ai fait quelque chose comme ça:

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

Voici le violon mis à jour: http://jsfiddle.net/H48sP/35/

Dans mon application, j'ai une directive indiquant où se trouvent ces sélections. Je fais donc element.find("select") au lieu de $("select") pour limiter la portée de l'élément sélectionné. Le texte est forcé de s'actualiser et s'affiche donc correctement après l'exécution de tous les cycles de résumé.

Si vous rencontrez le même problème, vous devrez peut-être ajouter un $timeout comme dans le violon et/ou vous devrez peut-être supprimer ultérieurement l'espace supplémentaire ajouté au texte de l'option si cela pose problème.

12
dnc253

L’autre soir, j’ai connu le même problème et, après avoir tout jeté sur la réflexion, j’en suis venu à la conclusion que IE ne souhaitait tout simplement pas gérer la mise à jour des filtres lors de l’utilisation de select.

Ma solution est de changer vos sélections pour ressembler à ceci:

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

Ils ont maintenant une classe et un changement ng sur eux. Ensuite, dans votre contrôleur, faites ce petit morceau de code amusant:

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

Ce qu'il fait est d'extraire les éléments du DOM et de les replacer au même endroit. Voici un violon qui fonctionne jsFiddle

Certaines des solutions que j’ai essayées et qui n’impliquaient pas le javascript consistaient par exemple à basculer l’affichage/la visibilité de la sélection. Avoir leur zIndex déplacé. La seule chose qui a certainement corrigé le problème était ce morceau de code.

29
Mathew Berg

J'ai la solution. 

Nous devons ajouter et supprimer la liste des options pour déclencher le rendu dans 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);
                    });
                }
            }
        }
    ]);
20
kkurni

Ajouter quelques lignes aux endroits suivants (marqués en gras par **) dans la fonction de rendu de selectDirective dans angular.js a bien fonctionné pour moi. Je cherche s’il existe une autre solution que le patching angularJS ou le forEach donnée ci-dessous?

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

et

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

Le problème était que l'attribut label de HTMLOptionElement n'est pas le même que l'attribut text si label est vide dans IE.

Cela peut être vérifié en ajoutant le code suivant après le chargement de l'écran et en regardant la console Web de FF et IE pour voir la différence. Si vous supprimez la mise en commentaire de la dernière ligne où le libellé est défini sur text, cela fonctionne correctement. Sinon, corrigez angular.js comme ci-dessus.

// 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. S. Ranjan

j'ai trouvé le même bogue dans IE avec select ..

si vous instanciez un SELECT comme (style jQuery):

$select = $template.clone();

et ensuite faire:

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

vous aurez le bug, décrit ci-dessus ..

MAIS, si vous instanciez

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

aucun bug ne s'est produit :)

3
enslaved

Le problème semble être lié à l'ordre des options retournées par le filtre. Lorsque vous définissez la dernière option sur A, les autres options de sélection sont modifiées. Ce qui semble poser problème pour IE est que l’option sélectionnée change de lieu. Dans la première zone de sélection, C est sélectionné parmi les options dans l'ordre suivant: A, B, C, D. L'option sélectionnée est la troisième option. Lorsque vous modifiez la quatrième zone de sélection de G à A, le filtre modifie les options de la première zone en B, C, D, G. L'option sélectionnée est maintenant l'option second, ce qui pose un problème avec IE. Cela pourrait être un bogue dans Angular ou un comportement étrange dans IE. J'ai créé un fork qui contourne cela en m'assurant que l'élément sélectionné est toujours la première option parmi les options filtrées:

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

Mettre à jour:

J'ai fait du débogage et trouvé la ligne qui pose des problèmes dans IE, mais je ne comprends pas pourquoi. Cela ressemble à un bug de rendu ou quelque chose. J'ai créé une autre solution de contournement qui ne nécessite aucune réorganisation des options: il s'agit d'une directive qui surveille les modifications apportées à l'élément select. Si une modification est détectée, une option est ajoutée et supprimée immédiatement:

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

        });
      }
    }
  }
});

Il suffit d’ajouter ie-select-fix pour sélectionner des éléments dans 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/ (nouveau)

3
joakimbl

Oh, je vais en enfer pour le conseil suivant ...! 

J'ai essayé ces suggestions, mais aucune n'a fonctionné pour moi.

En fait, j'utilisais Angular pour renseigner des contrôles de sélection comportant plusieurs options.

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

Parfois, Angular peuplait ces contrôles, les nouvelles données apparaissaient, mais dans IE, vous ne pouviez pas faire défiler de haut en bas pour afficher toutes les options.

Mais si vous appuyez sur F12, modifiez la largeur et la remettez à sa largeur d'origine, IE réapparaitra dans la vie et vous pourrez faire défiler la liste de valeurs.

Ma solution a donc été d'appeler cela, une seconde environ après qu'Angular ait fini de remplir les contrôles:

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

(Je vous ai dit que c'était une solution risquée ..)

Une autre chose: rappelez-vous que dans IE11, navigator.appName renverra désormais NETSCAPE (plutôt que MSIE ou Microsoft Internet Explorer) ... soyez donc prudent lorsque vous testez si votre code est exécuté sur IE ou sur un navigateur correct. 

Tu as été prévenu..!!

2
Mike Gledhill

Sur les lignes de Mathew Berg answer, je l’ai modifié pour fonctionner avec la directive 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.
            }
        })
      }
   }
});

De cette manière, le correctif s'applique à tous les éléments select du projet et vous n'avez pas à modifier les balises HTML existantes. J'ai également utilisé la méthode suivante pour détecter la version IE afin de définir la variable isIE9 sur 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;
}
0
Pawan Pillai

Il semble que ie9 ait un problème avec l'index. Prenant le deuxième exemple et changez-le pour le code suivant cela a fonctionné:

  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/

0
seoservice.ch

Il existe un moyen moins coûteux d'appliquer le rendu du contrôle après l'ajout d'options dynamiques . Ainsi, au lieu d'insérer/de supprimer un élément factice dans la liste déroulante, vous pouvez réinitialiser les styles CSS qui entraînent le rendu du contrôle, par exemple.

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

Le rendu est mis à jour et synchronisé si vous modifiez un attribut. Une modification anodine peut consister à définir l'attribut selectedIndex sur sa propre valeur:

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();
0
Luchostein

J'ai une solution de contournement pour le problème de liste de sélection IE 

Avant correction: http://plnkr.co/edit/NGwG1LUVk3ctGOsX15KI?p=preview

Après réparation: http://plnkr.co/edit/a7CGJavo2m2Tc73VR28i?p=preview

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

});

Je viens d'ajouter une option factice pour que le dom rende la sélection et l'enlève. laissez-moi savoir que cela fonctionne pour vous 

0
apurba mandal

Je devais changer le champ d'application. $ Watch à champ. $ WatchCollection pour que la solution @kkurni ci-dessus fonctionne pour IE9. Je voulais juste aider les autres qui rencontraient encore des problèmes dans IE9 pour le rendu des options sélectionnées lors de la modification.

0
bazzinga