Inspiration pour ce code:
- https://stackoverflow.com/questions/12058896/restore-multiple-detached-elements-in-jquery
- Find a value in an array of objects in Javascript [duplicate]
- https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
Je pars d’un menu dont la structure HTML est la suivante (une liste imbriquée simple) : https://jsfiddle.net/xpvt214o/513005/.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<ul class="level0"> <li>level0 <ul class="level1"> <li>level1 <ul class="level2"> <li>level2</li> <li>level2</li> <li>level2</li> [...] </ul> </li> </li> </ul> |
Sous iPhone, le menu a visiblement trop d’entrées. En tout cas lorsqu’on l’affiche, le navigateur Safari crashe et recharge la page. J’ai remarqué au cours de mes tests qu’en supprimant le dernier sous-niveau d’arborescence (.level2
dans mon exemple), le navigateur ne crashait plus. J’ai donc mis en place un système de cache du dernier sous-niveau d’arborescence de mon menu.
Ce système consiste à mettre en cache dans un tableau javaScript array et à l’aide des méthodes .push()
et .detach()
(jQuery) tous les derniers sous-niveaux d’arborescence. Ils ne sont, de cette manière, plus présents dans le DOM lorsqu’on ouvre le menu qui se trouve ainsi beaucoup plus léger à manipuler.
A l’activation d’un sous-menu mis en cache, on le fait ré-apparaître dans le DOM en le sortant du tableau. On fait le lien entre la rubrique activée et la sous-rubrique correspondante via un système d’IDs:
1 2 3 4 5 6 7 8 9 10 11 12 |
// Mise en cache du niveau 2 (crash iOS) var $cdDropdown = $('#cdDropdown'); $cdDropdown.find('.level2').each(function(){ cachedMenuEntries.push($(this).detach()); }); $cdDropdown.on('DOMSubtreeModified', function(e) { if (e.target.innerHTML.length > 0) { $('a', '#cdDropdown').off('click', cachedMenu).on('click', cachedMenu); } }); // end: Mise en cache du niveau 2 (crash iOS) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
function goBack(e) { var eClosestUl = e.closest('ul'); if(eClosestUl.hasClass('level2')){ eClosestUl.parent('.has-children').parent('ul').removeClass('move-out'); eClosestUl.remove(); } else{ eClosestUl.addClass('is-hidden').parent('.has-children').parent('ul').removeClass('move-out'); } } function cachedMenu(e) { var element = $(e.currentTarget); if(!element.closest('li').hasClass('go-back') && element.closest('ul').hasClass('level1')) { var getEntryID = element.closest('li').attr('id'); matchValue = getEntryID.split('li-')[1]; $(cachedMenuEntries[searchStringInArray(matchValue, cachedMenuEntries)]) .removeClass('is-hidden') .insertAfter(element); } if(element.closest('li').hasClass('go-back')) { goBack(element); } } function searchStringInArray (str, strArray) { for (var j=0; j<strArray.length; j++) { if (strArray[j].context.id == 'ul-'+str) return j; } return -1; } |
On invoque la fonction searchStringInArray
pour rechercher dans l’intégralité des entrées du tableau cachedMenuEntries
l’information [*].context.id
qui va matcher avec l’ID de la rubrique que nous venons d’activer:
Axes d’amélioration
- Utiliser un tableau clé/valeur (key/value)
D’autres pistes si vous avez des crashes iOS
- Les crashes peuvent provenir de transitions CSS mal paramétrées; CSS GPU Animation: Doing It Right.