IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

treeview "dynamique" ?


Sujet :

JavaScript

  1. #1
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut treeview "dynamique" ?
    Salut,

    Bon je pense que c'est quelque chose de très difficile à faire...
    Alors connaissez-vous un code tout fait pour treeview "dynamique" ?

    Je dis "dynamique" mais je ne sais pas comment on appelle cela alors je m'explique :

    Dans un treeview classique on insert tous les éléments dans le DOM, cela peut être des <ul> et des <li>, exemple : https://www.w3schools.com/howto/tryi...ow_js_treeview.

    Mais dans le cas où il y a énormément d’éléments on ne peut pas tout mettre dans le DOM car ça consomme trop de ressources, cela devient trop lent alors on ne doit mettre qu'une partie de ces éléments dans le DOM : Par exemple si on a 7000 éléments et que la zone visible peut contenir 30 éléments eh bien on en met par exemple 31 dans le DOM et pour afficher les autres éléments on fait défiler la scrollbar...

    C'est très difficile à gérer il me semble, il y a beaucoup de calcul :

    - La scrollbar doit être fabriqué à part, on ne peut pas laisser le navigateur gérer cela pour nous car on a 7000 éléments mais seuls 31 éléments à la fois sont injectés dans le DOM.. Si on mettait les 7000 éléments alors pas de problème, on pourrait le laisser gérer... Ainsi pour le navigateur la taille total du document serait de 31 éléments alors qu'en fait elle est de 7000 éléments...

    On est donc obligé d'avoir une scrollbar à part avec un scrollHeight correspondant aux 7000 éléments...
    Et encore ça c'est dans le cas où tous les éléments sont dépliés (c'est la valeur max) mais si on en plie certains eh bien le scrollHeight doit diminuer (on doit déduire de 7000 le nombre d’éléments cachés à cause des "pliés").

    - Et là le code folding (plier/déplier) est beaucoup plus difficile à gérer, il y a beaucoup de calcul, là aussi on ne peut pas laisser le navigateur gérer cela pour nous car seuls 31 éléments à la fois sont injectés dans le DOM ---> on ne pas juste faire display: "none"/"block" car pour ça il faudrait que tous les éléments soient injectés dans le DOM...

    - Savoir quels sont ces 31 éléments (sur 7000) qui doivent être affichés (injecté dans le DOM) n'est pas évident : cela dépend de la position de la scrollbar et des plier/déplier...

    Est-ce que quelqu'un voit de quoi je parle ?
    Qu'en pensez-vous ?
    Merci.

  2. #2
    Membre averti
    Avatar de Sparky95
    Homme Profil pro
    Full Stack (web) developer
    Inscrit en
    Décembre 2016
    Messages
    379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Belgique

    Informations professionnelles :
    Activité : Full Stack (web) developer
    Secteur : Transports

    Informations forums :
    Inscription : Décembre 2016
    Messages : 379
    Points : 358
    Points
    358
    Par défaut
    Bonjour,
    Je ne penses pas que cela soit si difficile que cela. Les éléments comment les as tu? via un fichier json ou via une bdd?
    Dans le cas de la bdd il suffit de trier les résultats d'en prendre les x premiers et de retourner cela via php. => AJAX
    En json je suppose que cela doit être la même chose sauf que les branches sont déjà faites.
    En tout cas j'attaquerais cela comme ça perso

    Apres la manière dont tu as décrit le problème est un peu brouillon donc pas facile à comprendre :/ mais l'idée serrait pour moi de faire des récupérations de données via l'ajax en récupérant que se dont tu as besoin. Et la au plus tu optimisera ton code au plus vite cela ira. Penses aux algo de tri par exemple. Mais 7000 éléments ce n'est pas encore énorme non plus après il faut voir quel taille prends chaque élément.

    Une autre idée serrait de ne faire qu'un index des éléments ce qui veut donc dire du bête texte à analyser et trier pour ton navigateur ce qui va vite.

    Maintenant si tu as pressé ou que tu as simplement la flem :p
    https://ourcodeworld.com/articles/re...script-plugins
    https://www.jstree.com/

  3. #3
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Merci.
    Alors oui mon problème n'est pas facile à comprendre, je veux parler d'un treeview qui affiche tous les éléments (disons pour fixer les idées des dossiers et des fichiers), comme les treeview classiques, dans ces derniers tous les dossiers, sous-dossiers et fichiers sont insérés dans le DOM, cela peut être une liste <ul> pour les dossiers et <li> pour les fichiers... Comme dans l'exemple : https://www.w3schools.com/howto/tryi...ow_js_treeview...

    Tu vois bien qu'on a tous les éléments, on peut déplier ou plier les dossiers...

    Les données peuvent venir d'un JSON ou d'une liste ou autre peu importe car là c'est l'affichage dans une treeview qui m’intéresse...

    Alors on pourrait faire une treeview classique en injectant tous les éléments dans le DOM mais quand il y en a beaucoup, le navigateur rame, car il y a trop de balise dans le DOM...

    C'est le même problème que pour les éditeurs de code : si on met toutes les lignes de code dans le DOM alors le navigateur va sérieusement ralentir... C'est pourquoi dans les éditeurs de code modernes on injecte dans le DOM seulement un petit nombre de lignes à la fois...

    Ah je crains de ne pas être clair...

  4. #4
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    16 955
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 16 955
    Points : 44 103
    Points
    44 103
    Par défaut
    Bonjour,
    je ne vois pas en quoi, à priori, le fait d'avoir 7000 éléments dans le document ferait ramer ta page, cela peut être long dans certains cas à l'affichage mais la gestion par le DOM de 7000 éléments n'est pas un soucis.

    Si l'on prend la page https://www.developpez.net/forums/, on à une NodeList avec plus de 6300 éléments.

    Un axe d'amélioration serait dans le code, si on reprend l'exemple que tu fournis
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    for (i = 0; i < toggler.length; i++) {
      toggler[i].addEventListener("click", function() {
        this.parentElement.querySelector(".nested").classList.toggle("active");
        this.classList.toggle("caret-down");
      });
    }
    de ne pas créer une fonction anonyme pour chaque éléments trouvés.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function handleClick() {
      this.parentElement.querySelector(".nested").classList.toggle("active");
      this.classList.toggle("caret-down");
    }
    for (i = 0; i < toggler.length; i++) {
      toggler[i].addEventListener("click", handleClick);
    }
    voir même utiliser la délégation.

  5. #5
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Merci.
    Alors oui je viens de re-tester avec 150 000 lignes ça rame un peu mais apparemment ça rame beaucoup dés que designMode passe à "on"... Donc c'est un des points que je n'avais plus en tête... Par contre avec l'autre solution j'ai testé avec plusieurs millions de lignes (avec 2 236 961 lignes c'est le max avec la scrollbar native mais on peut faire plus je pense) et ça marche mieux...

    En tous cas j'ai regardé le file treeview de VS Code et je ne vois qu'une partie des éléments dans le DOM, on voit les autres en déplaçant la scrollbar...

    Même principe que pour les éditeurs de code modernes, il n'y a que quelques lignes de code dans le DOM...

    A l'époque cela m'avait perturbé, je me disais que ces lignes devaient être quelque part cachés avec un display à "none" mais non en fait tout est en mémoire...


    Sinon oui tu as raison on peut améliorer certaines choses et effectivement utiliser la délégation me semble une bonne idée !

  6. #6
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Salut,

    Je suis tombé sur celui-ci : https://jquery-plugins.net/dynatree-...-jquery-plugin

    On peut lire cette note :
    Optimized for large dynamic trees (DOM elements are only created when really needed).
    Je ne pense que ce soit exactement ce dont je parlais mais peut-être que c'est une solution intermédiaire ? Je n'ai pas encore vu comment utiliser cette option...

  7. #7
    Membre éclairé
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    335
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 335
    Points : 715
    Points
    715
    Par défaut
    Bonjour Beginner.,
    Par rapport à la problématique initiale, cela me paraît faisable avec une approche récursive.
    J'ai fait un test.
    Il y a du innerHTML, plein de variables globales, c'est peu paramétré, peu optimisé etc.
    Tout est à refaire, mais il y a peut-être des idées à retenir.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    <div id="cadreVisibilite" style="background-color:#F0F0F0;min-width:300px;height:300px;overflow:hidden;display:inline-block;"></div><div style="background-color:#F0F0F0;width:20px;position:relative;display:inline-block;"><div id="ascenseur" style="background-color:red;width:100%;position:absolute;"></div></div>
    <script>
    function creerElements()
    	{
    	function creerElement(elements)
    		{
    		var i;
    		var element;
     
    		elements[elements.length]=element={st_titre:(++numero),ouvert:false};
    		element.enfants=[];
    		if(compteur>0 && Math.random()<0.1)
    			{
    			compteur-=i=Math.min(compteur,1+Math.floor(nombreElements*Math.random()/5));
    			while(--i>=0)
    				{
    				creerElement(element.enfants);
    				}
    			}
    		}
     
    	var compteur;
    	var numero;
     
    	numero=0;
    	compteur=nombreElements;
    	while(--compteur>=0)
    		{
    		creerElement(elements);
    		}
    	}
    function calculerHauteurContenu()
    	{
    	function traiterElement(element)
    		{
    		var i;
     
    		nombre++;
    		if(element.ouvert===true)
    			{
    			i=element.enfants.length;
    			while(--i>=0)
    				{
    				traiterElement(element.enfants[i]);
    				}
    			}
    		}
     
    	var i;
    	var nombre;
     
    	nombre=0;
    	i=elements.length;
    	while(--i>=0)
    		{
    		traiterElement(elements[i]);
    		}
    	return nombre*hauteurLigne;
    	}
    function adapterAscenseur()
    	{
    	var rapport;
    	var style;
     
    	style=ascenseur.style;
    	rapport=hauteurVisible/(hauteurContenu=calculerHauteurContenu());
    	if(rapport<1)
    		{
    		style.visibility="visible";
    		style.height=(hauteurAscenseur=Math.max(hauteurMinimaleAscenseur,hauteurVisible*rapport))+"px";
    		style.top=(yAscenseur=Math.min(yHaut*(hauteurVisible-hauteurAscenseur)/(hauteurContenu-hauteurVisible),hauteurVisible-hauteurAscenseur))+"px";
    		}
    	else
    		{
    		style.visibility="hidden";
    		hauteurAscenseur=hauteurVisible;
    		yAscenseur=0;
    		}
    	genererHTMLElementsVisibles();
    	}
    function ouvrirElement(element)
    	{
    	element.ouvert=!element.ouvert;
    	adapterAscenseur();
    	}
    function genererHTMLElementsVisibles()
    	{
    	//optimisation à faire : arrêter dès qu'on dépasse la hauteur visible
    	function traiterElement(element,espaces)
    		{
    		var i;
    		var elementHTML;
     
    		if(y+hauteurLigne>yHaut && y<yHaut+hauteurVisible)
    			{
    			cadreVisibilite.appendChild(elementHTML=document.createElement("div"));
    			elementHTML.style.height=hauteurLigne+"px";
    			if(element.enfants.length>0)
    				{
    				elementHTML.style.cursor="pointer";
    				elementHTML.style.color="red";
    				elementHTML.innerHTML=espaces+(element.ouvert===true?"-":"+")+element.st_titre;
    				elementHTML.addEventListener("click",function(ev_){ouvrirElement(element);});
    				}
    			else
    				{
    				elementHTML.innerHTML=espaces+element.st_titre;
    				}
    			}
    		y+=hauteurLigne;
    		if(element.ouvert===true)
    			{
    			i=-1;
    			while(++i<element.enfants.length)
    				{
    				traiterElement(element.enfants[i],espaces+"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
    				}
    			}
    		}
     
    	var i;
    	var y;
     
    	yHaut=hauteurVisible===hauteurAscenseur?0:(hauteurContenu-hauteurVisible)*yAscenseur/(hauteurVisible-hauteurAscenseur);
    	cadreVisibilite.innerHTML="";
    	y=0;
    	i=-1;
    	while(++i<elements.length)
    		{
    		traiterElement(elements[i],"");
    		}
    	cadreVisibilite.firstChild.style.marginTop=(-yHaut%hauteurLigne)+"px";
    	}
    var cadreVisibilite=document.getElementById("cadreVisibilite");
    var ascenseur=document.getElementById("ascenseur");
    var hauteurVisible=parseInt(cadreVisibilite.style.height,10);
    ascenseur.parentNode.style.height=hauteurVisible+"px";
    var yHaut=0;
    var yAscenseur=0;
    var nombreElements=1000;
    var hauteurLigne=30;
    var hauteurContenu=0;
    var hauteurMinimaleAscenseur=20;
    var hauteurAscenseur=0;
    var elements=[];
    ascenseur.addEventListener("mousedown",function(ev_)
    	{
    	function positionner(ev_)
    		{
    		style.top=(yAscenseur=Math.min(hauteurVisible-hauteurAscenseur,Math.max(0,ev_.clientY-y)))+"px";
    		genererHTMLElementsVisibles();
    		}
     
    	var y;
    	var style;
     
    	style=this.style;
    	y=ev_.clientY-yAscenseur;
    	document.addEventListener("mousemove",positionner);
    	document.addEventListener("mouseup",function clicRelache(ev_)
    		{
    		this.removeEventListener("mousemove",positionner);
    		this.removeEventListener("mouseup",clicRelache);
    		});
    	});
    creerElements();
    adapterAscenseur();
    </script>

  8. #8
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Bonjour Loralina,

    Waaw, excellent ! Merci.

    Alors déjà le problème n'était pas facile à comprendre mais apparemment vous l'avez bien compris mais en plus vous donnez un code qui fait bien le job, j’avoue que je m'y attendais pas, ça a l'air plus simple quand on voit le résultat...

    Rien que pour faire la scrollbar personnalisée cela m'avait pris pas mal de temps... J'ai d'ailleurs réussi à comprendre cette partie de votre code seulement en ressortant celui que j'avais fait il y a quelques temps et l’astuce avec avec Math.min et Math.max (à la place des conditions if else) n'est pas évidente à comprendre si on ne la connait pas mais heureusement je l'avais découvert dans ce forum (NoSmoking l'avait utilisée et en on avait parlé avec psychadelic...).

    Il me reste à étudier le reste du code mais déjà ça permet de voir le résultat voulu ce qui permet de mieux comprendre le problème (avec une illustration c'est bien mieux qu'avec seulement un exposé).

    La fonction creerElements est bien pratique pour faire des tests, ça tombe bien...

    PS : J'aurais une question par rapport à ce passage de la fonction creerElements :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    if (compteur > 0 && Math.random() < 0.1) {
                compteur -= i = Math.min(compteur, 1 + Math.floor(nombreElements * Math.random() / 5));
                while (--i >= 0) {
                    creerElement(element.enfants);
                }
            }
    Il y a deux appels à Math.random mais ils peuvent renvoyer un nombre différent, non ? Le deuxième nombre pourrait ne pas respecter la condition Math.random() < 0.1, non ?
    Merci encore.

  9. #9
    Membre éclairé
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    335
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 335
    Points : 715
    Points
    715
    Par défaut
    Bonjour,
    Citation Envoyé par Beginner. Voir le message
    Il y a deux appels à Math.random mais ils peuvent renvoyer un nombre différent, non ? Le deuxième nombre pourrait ne pas respecter la condition Math.random() < 0.1, non ?
    Les deux Math.random() ont un objectif différent :
    - Le premier indique simplement si l'élément va avoir des enfants.
    - Le second permet de calculer le nombre d'enfants, sachant qu'on en met 1 au minimum.

    La répartition aléatoire n'est pas idéale je trouve. Une amélioration serait que la probabilité d'avoir des enfants diminue quand la profondeur augmente.

    Sinon, on peut rester sur l'ascenseur par défaut en utilisant une balise vide dont on fait varier la hauteur, dans ce style :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    <div id="cadreVisibilite" style="overflow-x:hidden;overflow-y:auto;background-color:#F0F0F0;min-width:300px;height:300px;display:inline-block;position:relative;"><div id="referenceHauteur" style="visibility:hidden;"></div><div id="contenu" style="overflow:hidden;position:absolute;width:100%;"></div></div>
    <script>
    function creerElements()
            {
            function creerElement(elements,probabiliteEnfants)
                    {
                    var i;
                    var element;
     
                    elements[elements.length]=element={st_titre:++numero};
                    if(compteur>0 && Math.random()<probabiliteEnfants)
                            {
                            element.ouvert=false;
                            element.enfants=[];
                            compteur-=i=Math.min(compteur,1+Math.floor(nombreElements*Math.random()/10));
                            while(--i>=0)
                                    {
                                    creerElement(element.enfants,probabiliteEnfants*0.6);
                                    }
                            }
                    }
     
            var compteur;
            var numero;
     
            numero=0;
            compteur=nombreElements;
            while(--compteur>=0)
                    {
                    creerElement(elements,0.1);
                    }
            }
    function calculerHauteurContenu()
            {
            function traiterElement(element)
                    {
                    var i;
     
                    nombre++;
                    if(element.ouvert===true)
                            {
                            i=element.enfants.length;
                            while(--i>=0)
                                    {
                                    traiterElement(element.enfants[i]);
                                    }
                            }
                    }
     
            var i;
            var nombre;
     
            nombre=0;
            i=elements.length;
            while(--i>=0)
                    {
                    traiterElement(elements[i]);
                    }
            return nombre*hauteurLigne;
            }
    function adapterAscenseur()
            {
            referenceHauteur.style.height=calculerHauteurContenu()+"px";
            genererHTMLElementsVisibles();
            }
    function modifierOuvertureElement(element)
            {
            element.ouvert=!element.ouvert;
            adapterAscenseur();
            }
    function genererHTMLElementsVisibles()
            {
            //optimisation à faire : arrêter dès qu'on dépasse la hauteur visible
            function traiterElement(element,espaces)
                    {
                    var i;
                    var elementHTML;
     
                    if(y+hauteurLigne>yHaut && y<yHaut+hauteurVisible)
                            {
                            contenu.appendChild(elementHTML=document.createElement("div"));
                            elementHTML.style.height=hauteurLigne+"px";
                            if(element.enfants!==undefined)
                                    {
                                    elementHTML.style.cursor="pointer";
                                    elementHTML.style.color="red";
                                    elementHTML.innerHTML=espaces+(element.ouvert===true?"-":"+")+element.st_titre;
                                    elementHTML.addEventListener("click",function(){modifierOuvertureElement(element);});
                                    }
                            else
                                    {
                                    elementHTML.innerHTML=espaces+element.st_titre;
                                    }
                            }
                    y+=hauteurLigne;
                    if(element.ouvert===true)
                            {
                            i=-1;
                            while(++i<element.enfants.length)
                                    {
                                    traiterElement(element.enfants[i],espaces+"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
                                    }
                            }
                    }
     
            var i;
            var y;
     
            contenu.innerHTML="";
            y=0;
            i=-1;
            while(++i<elements.length)
                    {
                    traiterElement(elements[i],"");
                    }
            contenu.style.top=yHaut+"px";
            contenu.firstChild.style.marginTop=(-yHaut%hauteurLigne)+"px";
            }
    var cadreVisibilite=document.getElementById("cadreVisibilite");
    var contenu=document.getElementById("contenu");
    var hauteurVisible=parseInt(cadreVisibilite.style.height,10);
    var yHaut=0;
    var nombreElements=1000;
    var hauteurLigne=30;
    var elements=[];
    cadreVisibilite.onscroll=function()
            {
            yHaut=this.scrollTop;
            genererHTMLElementsVisibles();
            };
    cadreVisibilite.scrollTop=0;
    creerElements();
    adapterAscenseur();
    </script>
    J'ai retouché la fonction de création des éléments.
    Elle n'est sans doute pas encore optimale, mais bon, relativement satisfaisante tout de même.

    J'ajoute (14h15) une version simplifiée réunissant la hauteur et le contenu en une balise :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    <div id="cadreVisibilite" style="overflow-x:hidden;overflow-y:auto;background-color:#F0F0F0;min-width:300px;height:300px;display:inline-block;"><div id="contenu"></div></div>
    <script>
    function creerElements()
            {
            function creerElement(elements,probabiliteEnfants)
                    {
                    var i;
                    var element;
     
                    elements[elements.length]=element={titre:++numero};
                    if(compteur>0 && Math.random()<probabiliteEnfants)
                            {
                            element.ouvert=false;
                            element.enfants=[];
                            compteur-=i=Math.min(compteur,1+Math.floor(nombreElements*Math.random()/10));
                            while(--i>=0)
                                    {
                                    creerElement(element.enfants,probabiliteEnfants*0.6);
                                    }
                            }
                    }
     
            var compteur;
            var numero;
     
            numero=0;
            compteur=nombreElements;
            while(--compteur>=0)
                    {
                    creerElement(elements,0.1);
                    }
            }
    function calculerHauteurContenu()
            {
            function traiterElement(element)
                    {
                    var i;
     
                    nombre++;
                    if(element.ouvert===true)
                            {
                            i=element.enfants.length;
                            while(--i>=0)
                                    {
                                    traiterElement(element.enfants[i]);
                                    }
                            }
                    }
     
            var i;
            var nombre;
     
            nombre=0;
            i=elements.length;
            while(--i>=0)
                    {
                    traiterElement(elements[i]);
                    }
            return nombre*hauteurLigne;
            }
    function adapterAscenseur()
            {
            contenu.style.height=calculerHauteurContenu()+"px";
            genererHTMLElementsVisibles();
            }
    function modifierOuvertureElement(element)
            {
            element.ouvert=!element.ouvert;
            adapterAscenseur();
            }
    function genererHTMLElementsVisibles()
            {
            //optimisation à faire : arrêter dès qu'on dépasse la hauteur visible
            function traiterElement(element,espaces)
                    {
                    var i;
                    var elementHTML;
     
                    if(y+hauteurLigne>yHaut && y<yHaut+hauteurVisible)
                            {
                            contenu.appendChild(elementHTML=document.createElement("div"));
                            elementHTML.style.height=hauteurLigne+"px";
                            if(element.enfants!==undefined)
                                    {
                                    elementHTML.style.cursor="pointer";
                                    elementHTML.style.color="red";
                                    elementHTML.innerHTML=espaces+(element.ouvert===true?"-":"+")+element.titre;
                                    elementHTML.addEventListener("click",function(){modifierOuvertureElement(element);});
                                    }
                            else
                                    {
                                    elementHTML.innerHTML=espaces+element.titre;
                                    }
                            }
                    y+=hauteurLigne;
                    if(element.ouvert===true)
                            {
                            i=-1;
                            while(++i<element.enfants.length)
                                    {
                                    traiterElement(element.enfants[i],espaces+"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
                                    }
                            }
                    }
     
            var i;
            var y;
     
            contenu.innerHTML="";
            y=0;
            i=-1;
            while(++i<elements.length)
                    {
                    traiterElement(elements[i],"");
                    }
            contenu.firstChild.style.paddingTop=(yHaut-yHaut%hauteurLigne)+"px";
            }
    var cadreVisibilite=document.getElementById("cadreVisibilite");
    var contenu=document.getElementById("contenu");
    var hauteurVisible=parseInt(cadreVisibilite.style.height,10);
    var yHaut=0;
    var nombreElements=1000;
    var hauteurLigne=30;
    var elements=[];
    cadreVisibilite.onscroll=function()
            {
            yHaut=this.scrollTop;
            genererHTMLElementsVisibles();
            };
    cadreVisibilite.scrollTop=0;
    creerElements();
    adapterAscenseur();
    </script>

  10. #10
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Salut,

    Merci.
    Oui en effet c'est plus simple comme ça...

    Ces astuces pour pouvoir utiliser une scrollbar native sont intéressantes, il fallait y penser ! J'ai déjà utilisé ce genre d'astuce mais je ne crois que j'y aurais pensé tout seul (j'ai découvert cela en regardant des éditeurs de code comme Ace ou codemirror).

    Ceci dit le code avec la scrollebar personnalisée reste intéressant, en plus d'être instructif il permet d'ajouter son propre style (chose possible nativement uniquement avec chrome me semble-t-il).

    Le code fonctionne bien, il manquait juste un petit morceau dans la fonction adapterAscenseur (j'ai remplacé contenu.style.height = calculerHauteurContenu(); par contenu.style.height = calculerHauteurContenu() + "px";...).

    J'ai remarqué le commentaire (//optimisation à faire : arrêter dès qu'on dépasse la hauteur visible) dans la fonction genererHTMLElementsVisibles... J'ai essayé de trouver une solution en plaçant à certains endroits une condition (if (y >= yHaut + hauteurVisible) { return; } mais cela n'a fonctionné que partiellement... Pas facile de stopper une fonction récursive au moment où on le souhaite...

  11. #11
    Membre éclairé
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    335
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 335
    Points : 715
    Points
    715
    Par défaut
    Bonjour,
    Citation Envoyé par Beginner. Voir le message
    j'ai remplacé contenu.style.height = calculerHauteurContenu(); par contenu.style.height = calculerHauteurContenu() + "px";...
    Merci pour la correction, j'ai mis les codes à jour.

    Citation Envoyé par Beginner. Voir le message
    Ceci dit le code avec la scrollebar personnalisée reste intéressant, en plus d'être instructif il permet d'ajouter son propre style (chose possible nativement uniquement avec chrome me semble-t-il).
    Oui, après, en fonction des besoins, le code va rapidement se complexifier :
    - Gestion de la molette et/ou des boutons fléchés, cela étant d'autant plus utile qu'à partir d'une certaine hauteur de contenu, la barre de défilement ne permet plus d'être précis.
    - Gérer le cas où la barre de défilement peut se déplacer sur une hauteur différente de la hauteur visible (généralement, quand il y a des boutons fléchés, la barre peut se déplacer sur une hauteur égale à la hauteur visible moins la hauteur des boutons). Pour cela, il suffit d'ajouter une nouvelle variable.
    - Empêcher la sélection des éléments de la page quand on déplace la barre de défilement.
    - Gérer le clic dans la zone de déplacement de la barre pour pouvoir défiler de page en page.
    ...

    Citation Envoyé par Beginner. Voir le message
    J'ai remarqué le commentaire (//optimisation à faire : arrêter dès qu'on dépasse la hauteur visible) dans la fonction genererHTMLElementsVisibles...
    A tester, mais je pense que ça pourrait donner quelque chose comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    function genererHTMLElementsVisibles()
    	{
    	function traiterElement(element,espaces)
    		{
    		var i;
    		var elementHTML;
     
    		if(y+hauteurLigne>yHaut && y<yHaut+hauteurVisible)
    			{
    			contenu.appendChild(elementHTML=document.createElement("div"));
    			elementHTML.style.height=hauteurLigne+"px";
    			if(element.enfants!==undefined)
    				{
    				elementHTML.style.cursor="pointer";
    				elementHTML.style.color="red";
    				elementHTML.innerHTML=espaces+(element.ouvert===true?"-":"+")+element.titre;
    				elementHTML.addEventListener("click",function(){modifierOuvertureElement(element);});
    				}
    			else
    				{
    				elementHTML.innerHTML=espaces+element.titre;
    				}
    			}
    		if((y+=hauteurLigne)>=yHaut+hauteurVisible)
    			{
    			return false;
    			}
    		if(element.ouvert===true)
    			{
    			i=-1;
    			while(++i<element.enfants.length)
    				{
    				if(traiterElement(element.enfants[i],espaces+"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;")===false)
    					{
    					return false;
    					}
    				}
    			}
    		return true;
    		}
     
    	var i;
    	var y;
     
    	contenu.innerHTML="";
    	y=0;
    	i=-1;
    	while(++i<elements.length)
    		{
    		if(traiterElement(elements[i],"")===false)
    			{
    			break;
    			}
    		}
    	contenu.firstChild.style.paddingTop=(yHaut-yHaut%hauteurLigne)+"px";
    	}
    Autres optimisations possibles :
    - Quand on clique sur un élément, ne pas recalculer la hauteur en repartant de zéro à chaque fois.
    - Conserver les éléments html générés plutôt que de tout recréer à chaque fois. L'impact sur la mémoire devrait être léger, sachant qu'on peut le mesurer si besoin.

  12. #12
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Salut,

    Encore merci.
    Ok je vais tester la nouvelle fonction.

    Citation Envoyé par Loralina Voir le message
    Oui, après, en fonction des besoins, le code va rapidement se complexifier :
    Oui en effet faire un code complet et polyvalent c'est un peu plus compliqué mais j'avais déjà fait un truc de ce genre (il n'est pas terminé, il manque des choses et il y a peut-être bien des bogues et des choses à revoir comme utiliser des class dérivées (utiliser extends et super) au lieu de ce que j'ai fait...).

    Voici le code (avec un exemple pour tester) :
    - La page test : https://344874.playcode.io
    - Le source dans l'éditeur : https://playcode.io/344874?tabs=scri...onsole,preview

    Citation Envoyé par Loralina Voir le message
    Gestion de la molette et/ou des boutons fléchés, cela étant d'autant plus utile qu'à partir d'une certaine hauteur de contenu, la barre de défilement ne permet plus d'être précis.
    Effectivement il faudrait que j'ajoute cette fonctionnalité...

    Citation Envoyé par Loralina Voir le message
    Gérer le cas où la barre de défilement peut se déplacer sur une hauteur différente de la hauteur visible (généralement, quand il y a des boutons fléchés, la barre peut se déplacer sur une hauteur égale à la hauteur visible moins la hauteur des boutons). Pour cela, il suffit d'ajouter une nouvelle variable.
    Ah oui tout-à-fait, ça normalement c'est bon, ça a l'air de fonctionner : dans l'exemple dont j'ai donné le lien ci-dessus j'ai mis trois scrollBar verticales de trois tailles différentes égale|plus petit|plus grand...

    Citation Envoyé par Loralina Voir le message
    Empêcher la sélection des éléments de la page quand on déplace la barre de défilement.
    Oui on avait déjà rencontré ce problème dans un autre fil, on avait parfois un glisser/déposer qui était déclenché et provoqué alors un bogue...

    Dans le code j'ai ajouté ça :
    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // Supprime la selection pour empécher un glisser/déposer (drag&drop), à faire avec du CSS ?
        window.getSelection().removeAllRanges();

    Ça a l'air de suffire...
    Citation Envoyé par Loralina Voir le message
    Gérer le clic dans la zone de déplacement de la barre pour pouvoir défiler de page en page.
    Alors cela je l'ai fait sauf que cela ne va pas de page en page, j'ai fait comme dans VS Code : au clique, la bar (l’ascenseur) se déplace d'un coup de sorte que son milieu se retrouve là où on a cliqué (quand c'est possible bien sûr).

    Citation Envoyé par Loralina Voir le message
    A tester, mais je pense que ça pourrait donner quelque chose comme ceci :
    Merci.

    Citation Envoyé par Loralina Voir le message
    Autres optimisations possibles :
    - Quand on clique sur un élément, ne pas recalculer la hauteur en repartant de zéro à chaque fois.
    - Conserver les éléments html générés plutôt que de tout recréer à chaque fois. L'impact sur la mémoire devrait être léger, sachant qu'on peut le mesurer si besoin.
    Oui bonnes idées, j'avais pensé à la première idée et la deuxième il semble que c'est ce que font certains éditeurs de code...

    Merci encore.

  13. #13
    Nouveau membre du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2018
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2018
    Messages : 41
    Points : 33
    Points
    33
    Par défaut
    A mon humble avis :

    - tu peux stocker 10000 éléments (textes + urls) dans une structure de données, JS ne bronchera pas

    - par contre pour ce qui est de l'affichage : définir une "fenêtre de tir", c-à-d savoir ce qui est couramment A AFFICHER, et ne placer dans le DOM que ce qui est dans cette fenêtre, en déchargeant du DOM ce qui est replié par l'utilisateur

    - enfin en se souvenant qu'un arbre est juste une application du pattern Composite, on pourrait peut être le stocker sous une forme linéaire dans un tableau

    NOTE : Les arbres binaires peuvent être stockés dans un tableau plan. Il suffit de mettre les fils du nœud "i" aux adresse 2*i et 2*i+1. Pour les arbres n-aires comme une treeview, c'est plus compliqué... on peut tenter un tableau de blocks (c'est le sujet de mes recherches)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
          const Block = (uid, parent_uid, title, url) => {
          uid, 
          parent_uid,
          title,
         url,
         collapsed: false,
         on_screen: true
       }
    ... et de stocker tous ces blocs dans un trableau

    Si le balisage ressemble, pour chacun des éléments :

    Code HTML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      <div>
        <p>{title}</p>
         <ul>
            <li>
                <div> sous-element-1</div>
                <div> sous-element-2</div>
                <div> sous-element-3</div>
            <li>
          </ul>
       </div>

    ... alors en retenant l'adresse du premier élément affiché et en ajoutant 1 à chaque sous-élément activé, alors il devrait être possible de dénoter les30-40 éléments à afficher et devant être affichés.

    Voilà, simple avis parmi d'autres, en espérant que cela te mette sur la bonne piste, bon courage, F-E

  14. #14
    Membre éclairé
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    335
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 335
    Points : 715
    Points
    715
    Par défaut
    Bonjour,
    Citation Envoyé par Beginner. Voir le message
    Une remarque :
    Ecrire juste this._clientSize; sans affectation ne crée pas la propriété, à la différence de this._clientSize=undefined; (ou toute autre valeur comme null, 0, "" etc.).
    C'est très différent de var _clientSize; (ou let), qui crée bien la variable, même sans affectation.

    Citation Envoyé par Beginner. Voir le message
    Dans le code j'ai ajouté ça :
    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // Supprime la selection pour empécher un glisser/déposer (drag&drop), à faire avec du CSS ?
        window.getSelection().removeAllRanges();
    Je tenterais bien un événement "selectstart" avec un preventDefault.

  15. #15
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Salut,
    Citation Envoyé par francortes Voir le message
    - tu peux stocker 10000 éléments (textes + urls) dans une structure de données, JS ne bronchera pas
    Oui oui, je sais bien, j'ai même fait des testes avec plusieurs millions... En fait c'est comme ça dans les éditeurs de code, on peut avoir plusieurs millions de lignes et en plus pour chaque ligne il y a d'autres infos comme ceux nécessaires pour faire la surbrillance syntaxique, il y a aussi un historique des undo/redo qui peut consommer pas mal de mémoire...

    Citation Envoyé par francortes Voir le message
    par contre pour ce qui est de l'affichage : définir une "fenêtre de tir", c-à-d savoir ce qui est couramment A AFFICHER, et ne placer dans le DOM que ce qui est dans cette fenêtre, en déchargeant du DOM ce qui est replié par l'utilisateur
    Ben oui c'est un des sujets du fil justement et c'est ce que fait entre autres le code proposé par Loralina...

    Citation Envoyé par francortes Voir le message
    enfin en se souvenant qu'un arbre est juste une application du pattern Composite, on pourrait peut être le stocker sous une forme linéaire dans un tableau
    Oui effectivement j'y ai pensé et j'avais déjà fait ce genre de chose, il suffit par exemple de mettre chaque ligne dans l'ordre avec la profondeur...

    Mais si on veut optimiser il faudrait ajouter des infos, il faudrait par exemple la liste de tous les dossiers avec la possibilité de connaitre le nombre d’éléments dans chaque dossier, le but c'est d’éviter par exemple de scruter tous les éléments du tableau, par exemple si un dossier est fermé inutile de parcourir tous les éléments qu'il contient...
    Citation Envoyé par francortes Voir le message
    on peut tenter un tableau de blocks (c'est le sujet de mes recherches)
    Recherches intéressantes !

    Citation Envoyé par Loralina Voir le message
    Une remarque :
    Ecrire juste this._clientSize; sans affectation ne crée pas la propriété, à la différence de this._clientSize=undefined; (ou toute autre valeur comme null, 0, "" etc.).
    C'est très différent de var _clientSize; (ou let), qui crée bien la variable, même sans affectation.
    Oui effectivement c'est pour ça que j'ai ajouté un peu plus loin ceci :
    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // Cette fonction est exécutée ici pour intialiser les autres propriétés (this._clientSize, this._scrollSize, this._pos, this._posMax, this._factor...) ce qui est nécéssaire notamment pour qu'elles soient affectées à l'instance...
            this._update(elem);

    Et si j'ai déclaré les variables avec leur JSdoc c'est pour l'assistance dans VSCode (autocomplétion, hover...).


    Citation Envoyé par Loralina Voir le message
    Je tenterais bien un événement "selectstart" avec un preventDefault.
    J'ai aussi vu cela en plus de ce que j'avais ajouté mais apparemment supprimer la sélection a suffit alors je m'en suis contenté...

Discussions similaires

  1. TreeView & template dynamique
    Par beekeep dans le forum Windows Presentation Foundation
    Réponses: 2
    Dernier message: 05/03/2012, 19h38
  2. cherche exemple de treeview+struts dynamique
    Par debut_java dans le forum Struts 1
    Réponses: 7
    Dernier message: 03/09/2007, 17h20
  3. [treeview] chargement dynamique
    Par thewaterkidny dans le forum ASP.NET
    Réponses: 4
    Dernier message: 03/05/2007, 15h30
  4. Treeviews crées Dynamiquement
    Par Cpt Anderson dans le forum Delphi
    Réponses: 3
    Dernier message: 06/09/2006, 14h46
  5. [VB.NET] Treeview creer dynamiquement a partir d'une bd
    Par graphicsxp dans le forum Windows Forms
    Réponses: 3
    Dernier message: 31/03/2005, 17h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo