Bonjour,
J'ai un textarea pour l'édition en bbcode.
Je voudrais ajouter une commande annuler et une commande rétablir. Mes recherches sont vaines. Je ne sais absolument pas comment démarrer. Pourriez-vous m'aider, si possible en JavaScript pur.
Bonjour,
J'ai un textarea pour l'édition en bbcode.
Je voudrais ajouter une commande annuler et une commande rétablir. Mes recherches sont vaines. Je ne sais absolument pas comment démarrer. Pourriez-vous m'aider, si possible en JavaScript pur.
Bonsoir à tous,
Une fonction Undo Redo: Javascript ES6 Undo Redo Function
J'ai essayé d'implémenter la méthode indiquée plus une autre (voir plus bas) et je ne m'en sors pas. Je DESESPERE COMPLETEMENT.
J'essaie d'adapter le script à mon cas et je ne m'en sors pas.
Contrairement à l'exemple, je n'ai qu'une zone de texte qui sert à la fois à la saisie et à l'affichage. Je n'ai pas de bouton add et je dois à la place détecter les changements dans la zone de texte.
J'ai essayé d'adapter le script mais je ne m'en sors pas.
J'ai en outre une autre difficulté qui est que je n'arrive pas à me familiariser avec l'écriture des fonctions fléchées et à l'affectation des fonctions à des variables mais ça c'est une autre histoire.
EDIT: J'ai trouvé un autre exemple plus simple qui fonctionne pour la fonction undo mais que je n'arrive pas à faire fonctionner pour la fonction redo. En effet, le dernier élément de commands n'est plus disponible comme le montre le console.log(commands); de la ligne 35. J'ai essayé de cloner commands avant la ligne 17 pour conserver l'état initial de commands mais j'accumule les erreurs. Par ailleurs la fonction undo ne fonctionne pas lorsque j'insère une balise bbcode. JE DESESPERE.
Voici mon code tel que je l'ai adapté:
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 const textArea = document.getElementById("textArea"); const commands = []; const btnUndo = document.getElementById('btnUndo'); const btnRedo = document.getElementById('btnRedo'); function saveCommand(e) { commands.push({ // the action is also saved for implementing redo, which // is not implemented in this example. action: { type: 'add', key: e.key, index: textArea.selectionStart }, inverse: { type: 'remove', index: textArea.selectionStart } }) } function undo() { let value = textArea.value.split('') const lastCommand = commands.pop() if (!lastCommand) return switch (lastCommand.inverse.type) { case 'remove': value.splice(lastCommand.inverse.index, 1) break; } textArea.value = value.join('') } btnUndo.addEventListener('click', function(e){ undo(); }, false ); function redo() { console.log(commands); // ?? } btnRedo.addEventListener('click', function(e){ redo(); }, false ); textArea.addEventListener('keydown', function(e){ saveCommand(e); }, false );
Bonjour,
il existe quelque chose de très simple :
sinon il me semble plus judicieux d'observer l'événement beforeinput voire input sur la <textarea>.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 // annuler document.execCommand("undo"); // rétablir document.execCommand("redo");
Je lis dans la doc MDN ceci:Du coup, je ne sais pas quoi faire. Je galère vraiment.Obsolète: Cette fonctionnalité a été supprimée des standards du Web. Bien que quelques navigateurs puissent encore la supporter, elle est en cours d'éradication. Ne l'utilisez ni dans d'anciens projets, ni dans de nouveaux. Les pages et applications Web l'utilisant peuvent cesser de fonctionner à tout moment.
A défaut de quelque chose d'aussi simple, j'aimerais trouver un script complet existant qui gère aussi l'incorporation de balises bbcode complètes.
EDIT: J'ai quand même fait l'essai avec Edge et ça ne fonctionne pas avec l'insertion de balises bbcode par un bouton.
Bonjour,
Tu ne peux pas utiliser simplement la propriété value du textarea pour enregistrer et restaurer le contenu ?
Un petit exemple : https://jsfiddle.net/mxkeyLj1/
Cette méthode est déconseillée sur les tutos que j'ai lus parce qu'elle est gourmande en ressources. D'autre part, elle ne fonctionne pas pour les bbcodes introduits par un bouton.
Salut,
Ben je ne sais ce que tu veux faire exactement mais tout est relatif... Est-ce que les textes tapés dans la textearea sont si énormes que ça ? La solution "simple" nécessite effectivement de sauvegarder tout le contenu de la textearea à chaque changement mais par exemple si tu choisis une pile de 300 (actions) et qu'en moyenne tes textes font environ 5 000 caractères ça te fera peut-être environ 3Mo (en supposant qu'un caractère occupe deux octes), les éditeurs de codes consomment bien plus que ça...
J'avais commencé à en faire un il y a quelques temps et je pouvais stocker en mémoire des millions de lignes sans pb... Ce qui pose problème c'est quand on charge trop le DOM c'est pour ça qu'on stocke la majorité des lignes en mémoire et on affiche dans le DOM que ce qui est nécessaire...
L'autre solution est meilleure mais elle aussi bien plus compliquée et de toute façon tu auras besoin dans les deux cas de gérer la pile et l'historique alors tu peux mettre cela au point avec la solution simple et ensuite si tu y tiens toujours tu passes à la solution optimisée...
Ben tu vois ça c'est un point à régler avec la solution simple, ça te permettra de comprendre le principe...
Quand tu comprendras le principe alors tu sauras que c'est à toi de tout gérer, exemple : je suppose que tu ajoutes une balise bbcode au clique sur le bouton ?
Bon ben cela va modifier le texte eh bien c'est à toi de stocker le texte juste avant sa modification dans la pile des actions.
Alors si tu veux défaire la modification tu recharges la textearea avec le texte que tu as précédemment stocké, tu comprends ?
Et ça c'est le plus simple...
Avec l'autre solution tu ne stockes pas tout le texte mais seulement le changement : tu repères la partie qui a changé, tu donnes un nom à toutes tes actions, par exemple ici "insertion_balise", tu stockes les coordonnées des chaines de caractères insérées, et tu dois écrire une fonction qui défait cette action, cette fonction recevra les coordonnées, comme ça tu pourras repérer les chaines de caractères insérées et les remplacer par des chaines vides ""...
Par contre si tu supprimes une chaine c'est une autre action, tu lui donnes aussi un nom et tu écris une fonction qui défait cette action, ici elle recevra la chaine supprimée et ses coordonnées ainsi tu pourras insérer cette chaine supprimée aux coordonnées enregistrées dans la pile...
Et ainsi de suite pour toutes les actions...
J'ai essayé d'écrire ceci:La fonction undo() dépend du nombre d'action sur le bouton undo:
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 // Undo/redo treatment const textArea = document.getElementById("textArea"); const contents = []; const btnUndo = document.getElementById('btnUndo'); const btnRedo = document.getElementById('btnRedo'); var lastContent ; // = ''; function saveContent(){ contents.push(textArea.value); // Ajoute un élément } function undo(){ lastContent = contents.pop(); // Supprime le dernier élément et le met dans lastContent console.log(lastContent); // jklm console.log(contents); // ['j', 'jk', 'jkl'] textArea.value = lastContent ? lastContent : textArea.value; //textArea.value = lastContent ? contents.pop() : textArea.value; } function redo(){ console.log(textArea.value); console.log(lastContent); // undefined contents.push(lastContent); console.log(contents); // undefined console.log(textArea.value); // } //textArea.addEventListener('beforeinput', function(e) textArea.addEventListener('keyup', function(e){ saveContent(); }, false ); btnUndo.addEventListener('click', function(e){ undo(); }, false ); btnRedo.addEventListener('click', function(e){ redo(); }, false );
- A la première action, rien ne se passe,
- A la deuxième action le fonctionnement devient normal (effacement du dernier caractère)
Si je remplace la ligne 17 par la ligne 18, la première action est correcte, mais la deuxième efface deux caractères.
Je ne sais pas comment faire.
J'ai réussi à faire un script qui marche pour la saisie directe de caractères mais qui ne fonctionne pas en redo pour le bbcode.
Si je remplace la ligne 29 par la ligne 28, j'ai des anomalies de fonctionnement: Le dernier caractère saisi ne réapparait pas en redo.
Il doit donc y avoir quelque chose qui ne fonctionne pas dans la fonction redo() mais je ne vois pas quoi.
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 // Undo/redo treatment const textArea = document.getElementById("textArea"); const contents = []; const btnUndo = document.getElementById('btnUndo'); const btnRedo = document.getElementById('btnRedo'); const previous = textArea.value; var contIndex = 0; function saveContent(){ contents.push(textArea.value); contIndex++; } function undo(){ if(contIndex>0) contIndex--; textArea.value = contIndex>0 ? contents[contIndex-1] : previous; } function redo(){ console.log(contents); if (contIndex < contents.length){ contIndex++; textArea.value = contents[contIndex-1]; } } //textArea.addEventListener('beforeinput', function(e){ // m'a été conseillé mais fonctionne mal textArea.addEventListener('keyup', function(e){ saveContent(); }, false ); btnUndo.addEventListener('click', function(e){ undo(); }, false ); btnRedo.addEventListener('click', function(e){ redo(); }, false );
Salut,
Oui le deuxième code me semble meilleur, tu as compris qu'il fallait utiliser des "pointers"...
Sinon pour ton problème il faudrait qu'on puisse tester, il faudrait que tu donnes le html et la partie JS concernant l'insertion des balises...
PS : Apparemment tu ne fixes pas de limites à ta pile ?
Il me semble que certains fixent une limite genre 300 (actions) et après cela on reboucle au début, l'action 301 est stockée à l'indice zéro de la pile (du coup la première action est perdue (écrasée)).
Il n'y a pas que Edge, les autres aussi la gestion interne des boîtes de saisie, via execCommand échappe au événement que tu pourras mettre sur celles-ci.EDIT: J'ai quand même fait l'essai avec Edge et ça ne fonctionne pas avec l'insertion de balises bbcode par un bouton.
Notons que souvent cela est suffisant même si le « Undo/Redo » s’applique au document en entier.
Si tu veux faire « simple » dans ce cas utilise des éditeurs tout fait comme CKeditor, TinyMCE.A défaut de quelque chose d'aussi simple, j'aimerais trouver un script complet existant qui gère aussi l'incorporation de balises bbcode complètes.
Quoiqu'il arrive, il te faut avoir une vision globale de ton projet, pas petit bout par petit bout.
Concernant l'affectation de la value via un textarea.value = "la nouvelle valeur" ne déclenche aucun événement, il te faudra donc en tenir compte également.
Concernant le « Undo/Redo » il faut effectivement gérer cela via une « Stack » et là encore il te faut bien savoir ce que tu souhaites défaire/refaire, texte et position du curseur sont à priori un minimum, c'est là qu'intervient l'écoute beforeinput.
Il te faut également tenir compte, ou pas, des Ctrl + Y, Ctrl + Z pour lesquelles par contre tu reçois un événement.
Une chose qu'il faut également voir c'est le fonctionnement d'un système « Undo/Redo », toute nouvelle entrée supprime de la pile les « Redo » en attente. Cela diminue forcément la taille en mémoire de la stack.
Je te mets un exemple de gestion simple adapté aux <textarea> :
...c'est évolutif et perfectible bien sûr !
Si tu veux faire quelque chose qui tienne la route, cela ne sera pas forcément simple, demande à Beginner qui sait de quoi il parle et qui donc de bons conseils![]()
Salut,
Apparemment même si on utilise une balise éditable (contenteditable="true"), toutes les actions exécutées par un script ne seront pas prises en compte nativement... Il faudrait alors ajouter nous même ces actions dans la pile native...
Il y a une grosse doc sur ça (ici), l'introduction explique le pb :
Mais il semble que ces fonctionnalités ne soient pas implémentées... Si c'est bien le cas ben ça ne risque pas de changer vue que la fonction execCommand est devenue obsolète...This specification defines the API to manage user agent's undo transaction history (also known as undo stack) and make objects that can be managed by the undo transaction history.
Many rich text editors on the Web add editing operations that are not natively supported by execCommand and other Web APIs. For example, many editors make modifications to DOM after an user agent executed user editing actions to work-around user agent bugs and to customize for their use.
However, doing so breaks user agent's native undo and redo because the user agent cannot undo DOM modifications made by scripts. This forces the editors to re-implement undo and redo entirely from scratch, and many editors, indeed, store innerHTML as string and recreate the entire editable region whenever a user tires to undo and redo. This is very inefficient and has limited the depth of their undo stack.
Also, any Web app that tries to mix contenteditable region or text fields with canvas or other non-text editable regions will have to reimplement undo and redo of contenteditable regions as well because the user agent typically has one undo transaction history per document, and there is no easy way to add new undo entry to the user agent's native undo transaction history.
This specification tries to address above issues by providing ways to define undo scopes, add items to user agent's native undo transaction history, and create a sequence of DOM changes that can be automatically undone or redone by user agents.
Oui et ce qui est sûr c'est qu'il faudra dans tous les cas annuler le fonctionnement par défaut (ce que tu as fait apparemment) car sinon il y aura interférence entre les deux piles d'actions... Et perso je le mettrais car ces raccourcis clavier sont bien pratiques ...
Ah ben justement je m' nterrogeais sur cela car j'ai vu certains codes qui ne le font pas : c'est le cas du code de "moimp" et aussi de celui-ci apparemment : Demo (source du code)...
Mais la plupart des autres éditeurs font ce que tu dis... Bon ces autres éditeurs optimisent aussi : par exemple taper plusieurs lettres à la suite compteront pour une seule action...
Waw, excellent ! En plus c'est bien présenté comme d'habitude...
J'ai testé les deux. CKEditor est complet et répond à mon besoin.
J'ai cependant une première difficulté avec l'erreur suivante dans la console:J'ai cherché parmi les éléments téléchargés et sur le site de CKEditor et je ne trouve aucune trace de ce CKEDITOR (en majuscules).Uncaught ReferenceError: CKEDITOR is not defined at main.php?page=7:86:10 ckeditor.js:28 Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened. (anonymes) @ ckeditor.js:28
Salut
Une traduction automatique donne
De plus ce lien CKEditor 5 te serait-t-il utile ?Erreur de référence non interceptée: CKEDITOR n'est pas défini au principal.php?page=7:86:10 ckeditor.js:28 Échec de l'exécution de 'write' sur 'Document': il n'est pas possible d'écrire dans un document à partir d'un script externe chargé de manière asynchrone à moins qu'il ne soit explicitement ouvert. (anonymes) @ ckeditor.js:28
J'avais très bien compris le message d'erreur.
Par contre, j'avais fait mon installation à partir d'une version française qui malheureusement ne permet pas de télécharger la dernière version mais la version 4.19.
Le lien vers CKEditor 5 renvoie bien vers la dernière version que j'ai bien téléchargé.
Par contre, j'ai des difficultés avec l'installation et le tutoriel qui donne une surabondance de liens et d'informations qui ne permettent pas de suivre une procédure pas-à-pas. En particulier, je n'arrive pas à configurer l'application et à l'intégrer dans mon projet: Dans l'exemple de la page html je ne trouve pas la source du script. <script src="dist/bundle.js"></script> qui ne figure pas dans le fichier zip téléchargé.
Je voudrais éviter d'utiliser npm que je ne souhaite pas installer.
Salut,
Oui il y a de grande chance qu'il réponde à ton besoin, il existe depuis un moment, il est connu... Il y en a d'autres, il y a cette article qui fait une comparaison : https://quilljs.com/guides/compariso...h-text-editors
Il existe aussi des éditeurs plus légers...
Bon ben le code de NoSmoking reste utile et instructif...
Finalement après analyse, je me suis rendu compte que ce code le fait aussi, c'est dans la démo qu'il y a un bug...
Compte tenu de tout ce qui précède, je vais implémenter CKEditor.
J'y vois deux avantages:
- C'est un produit éprouvé et maintenu.
- Il est WYSIWYG.
Par contre, je n'arrive pas à l'installer et je butte sur le point cité en #16 modifié.
Voilà une décision qu'elle est sage même si de réinventer la roue est toujours enrichissantCompte tenu de tout ce qui précède, je vais implémenter CKEditor.
si tel est ton choix !!!!Je voudrais éviter d'utiliser npm que je ne souhaite pas installer.
Regarde du côté de la documentation officielle : Installation example, l'exemple est simple à comprendre.
Merci! J'y suis presque. Tout fonctionne avec l'exemple et le 'build' 'decoupled-document'.
Par contre après avoir regarder la doc, je n'arrive pas à paramétrer la langue ni à supprimer des boutons de commande. Dans le code ci-dessous, la ligne 6 n'est pas prise en compte.
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 <div id="toolbar-container"></div> <div id="editor"></div> <script src="../js/setHomepage.js"></script> <script src="https://cdn.ckeditor.com/ckeditor5/34.1.0/decoupled-document/ckeditor.js"></script> <script src="https://cdn.ckeditor.com/ckeditor5/34.1.0/decoupled-document/translations/fr.js"></script> <script> DecoupledEditor .create( document.querySelector( '#editor' ) ) .then( editor => { const toolbarContainer = document.querySelector( '#toolbar-container' ); toolbarContainer.appendChild( editor.ui.view.toolbar.element ); } ) .catch( error => { console.error( error ); } );</script>
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager