Editeur wysiwyg minimal & mise à jour BDD par ajax en focusout

Eléphant du PHP | 300 Messages

12 sept. 2019, 14:12

Bonjour à tous,

Je travaille actuellement sur un petit éditeur de texte en wysiwyg, avec une mise en forme minimale (juste gras, italique, liste à puce et lien hypertexte). Le texte se trouve dans un div en contenteditable=true, et la mise à jour dans la base de données se fait grâce à une requête en Ajax.

Voici les différents scripts (je vous épargne le CSS) :


La page sur laquelle se trouvent les champs à éditer :

Code : Tout sélectionner

// Toolbar echo "<div id='toolbar'>"; echo "<a href='#' data-command='undo'></a>"; echo "<a href='#' data-command='redo'></a>"; echo "<a href='#' data-command='bold'></a>"; echo "<a href='#' data-command='italic'></a>"; echo "<a href='#' data-command='insertUnorderedList'></a>"; echo "<a href='#' data-command='insertOrderedList'></a>"; echo "<a href='#' data-command='createLink'></a>"; echo "<a href='#' data-command='unlink'></a>"; echo "</div>"; // Contenu à éditer echo "<article class='ARTICLE'>"; $result = mysqli_query($DB, "SELECT * FROM bdd_textes ORDER BY ordre ASC"); while($row = mysqli_fetch_array($result)) { echo "<div class='EDITLIVE' data-id='".$row['id']."' contenteditable='true'>".$row['texte']."</div>"; } echo "</article>";

Fichier ajax.editlive.js (inclus dans <head></head>) :

Code : Tout sélectionner

$(document).ready(function() { // ****** PRISE DE FOCUS $('.EDITLIVE').on('focusin', function() { // Apparition de la toolbar $('#toolbar').css('display', 'block'); // Définition de sa position (juste au dessus du champ d'édition) position = $(this).position(); toolbarX = position.left; toolbarY = position.top - 27; $('#toolbar').offset({ top:toolbarY, left:toolbarX }); }); // ****** APPUI SUR UN BOUTON DE LA TOOLBAR $('#toolbar a').click(function() { var command = $(this).data('command'); if(command == 'createlink') { url = prompt('Entrer le lien : ', 'http:\/\/'); document.execCommand($(this).data('command'), false, url); } else { document.execCommand($(this).data('command'), false, null); } // Supprime le comportement par défaut du lien event.preventDefault(); }); // ****** PERTE DE FOCUS : exécution de" _editlive.php" $('.EDITLIVE').on('focusout', function() { // Récupération de data['xxx'] ==> définit les valeurs de $_POST['xxx'] data = {}; data['valeur'] = $(this).html(); data['id'] = $(this).attr('data-id'); // Requête ajax et update BDD $.ajax( { type:"POST", url:"_editlive.php", cache:false, data:data, dataType:"text", success:function(reponse) { // Succès : exécute le script _editlive.php return true; }, error:function(reponse) { // Erreur : message d'alerte alert("Erreur update : id n°"+data['id']); return false; } }); }); });

FICHIER _editlive.php (mise à jour de la base de données) :

Code : Tout sélectionner

// INTERDICTION DE L'ACCES DIRECT A LA PAGE if(!(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest')) trigger_error('Accès refusé', E_USER_ERROR); $id = $_POST['id']; $valeur = $_POST['valeur']; // Suppression des éventuelles balises html non autorisées et des retours chariots $valeur = strip_tags($valeur, "<b><strong><i><em><br><ol><ul><li><a>"); $valeur = str_replace("\n", "", $valeur); // Update de la base de données mysqli_query($DB, "UPDATE bdd_textes SET texte=\"".addslashes($valeur)."\" WHERE id=".$id);

Voilà pour le code : tout fonctionne à merveille, la mise en forme en wysiwyg se passe bien, l'exécution du script en Ajax aussi, la base de données se met à jour lorsque je sors le focus du champ éditable. Le problème c'est pour faire disparaître la toolbar après édition : j'ai essayé de placer le code $('#toolbar').css('display', 'none'); à divers endroits, mais ce n'est pas satisfaisant.

Quand je mets le $('#toolbar').css('display', 'none'); avant ou après le "return true;" du script ajax, ça fonctionne mais la toolbar disparaît même si on a encore du texte à éditer (évidemment, puisqu'en cliquant dans la toolbar, on perd le focus et ça exécute le script). L'idée serait donc de faire exécuter le script du focusout SAUF si on clique sur un élément de la toolbar ou son conteneur parent.

Est-ce possible ? Merci pour votre aide !
Modifié en dernier par finipe le 12 sept. 2019, 14:58, modifié 1 fois.

Eléphant du PHP | 300 Messages

12 sept. 2019, 14:37

Bon je crois avoir des pistes...

Pour ne pas activer le script quand je clique sur la toolbar, j'ai ajouté ceci :

Code : Tout sélectionner

// Exécution du script seulement si l'élément cliqué // n'est pas la toolbar ou un de ses enfants $(document.body).click(function(e) { var toolbar = $('#toolbar'); if(!$(e.target).is(toolbar) && !$.contains(toolbar[0], e.target)) { // Ici le script onfocusout } }
Mais le problème, c'est que comme j'ai plusieurs champs avec la class "EDITLIVE", le script semble s'exécuter pour chaque champ ayant cette classe sur la page (ce qui est logique, forcément maintenant que je le sais :D)

Il faut donc que je parvienne à donner un identifiant unique au champ édité, mais j'ai un nombre variable de champs éditables sur une même page (d'où l'utilisation d'une classe et non d'un id) : comment puis-je faire ça ?

Eléphant du PHP | 336 Messages

13 sept. 2019, 13:58

Salut,

Tu peux utiliser les attributs data https://api.jquery.com/data/ ou juste une classe css que tu appelles disable-script-when-click par exempl.

Eléphant du PHP | 300 Messages

16 sept. 2019, 15:53

Rien à faire, j'ai essayé cinquante façons différentes, mais le problème vient toujours de la même chose (me semble-t-il) : j'ai plusieurs champs à éditer dans la même page, et le champ à éditer est identifié par sa CLASSE et non son id. Du coup c'est vaseux.

Mais je n'arrive pas à trouver de solution pour récupérer l'ID de l'élément dont je sors (onfocusout), et éviter les exécutions de script en cascade.

Eléphant du PHP | 300 Messages

16 sept. 2019, 16:01

En fait, le script fonctionne. C'est pas idéal parce qu'il s'exécute de nombreuses fois (je ne sais pas trop pourquoi) mais ça fonctionne tant que je ne fais pas disparaître la toolbar à l'exécution.

Mais dès que j'essaye de la virer (avec toolbar.hide() par exemple, ou même simplement en la déplaçant hors de l'écran sans la cacher) le script ne fonctionne plus.

Eléphant du PHP | 300 Messages

17 sept. 2019, 14:15

Bon je reviens avec quelques indices surprenants...

Voici le bout de code surprenant :

Code : Tout sélectionner

// Si on clique en dehors de la toolbar ou d'un de ses enfants if(!($(e.target).is(toolbar) || $.contains(toolbar[0], e.target))) { // Compte le nb d'exécution de cet endroit du script debug_cpt(); // Edition effective du champ texte $('.EDITLIVE').on('focusout', function() { // data['xxx'] ==> définit les valeurs de $_POST['xxx'] data = {}; data['valeur'] = $(this).html(); data['id'] = $(this).attr('data-id'); data['type'] = $(this).attr('data-type'); // Requête ajax et update BDD $.ajax( { type:"POST", url:"_editlive.php?config=qsn", cache:false, data:data, dataType:"text", success:function(reponse) { // Succès : exécute le script _editlive.php?config=qsn return true; }, error:function(reponse) { // Erreur : message d'alerte alert("Erreur update : "+data['type']+", id n°"+data['id']); return false; } }); }); } else debug("Clic dans la toolbar");


Lorsque je place la fonction debug_cpt() là où elle est placée dans le code ci-dessus, j'ai bien un compteur qui s'incrémente de 1 à chaque clic (sauf si je clique dans la toolbar). Le comportement est donc bien celui qui était attendu.

En revanche, si je place la fonction debug_cpt() dans le focusout de .EDITLIVE, alors j'ai un compte délirant qui augmente de façon exponentielle : 1, 4, 9, 16, 25, 36 (donc +3, +5, +7, +9, +11...). Je précise qu'à des fins de test je n'ai sur ma page qu'un seul champ avec la classe .EDITLIVE.

Quelqu'un y comprend quelque chose ? Parce que moi pas :lol:

Eléphant du PHP | 300 Messages

17 sept. 2019, 14:56

Problème résolu comme suit :

Code : Tout sélectionner

$('.EDITLIVE').on('focusout', function(e) { e.stopImmediatePropagation(); // ... suite du code }
Il ne me reste plus qu'à éclaircir le mystère des tous débuts : quand je clique dans la toolbar, il exécute quand même le code de focusout, alors que j'ai mis une condition pour empêcher cela.

J'ai testé la condition, et elle s'exécute ! Donc en fait, elle s'exécute (je clique dans la toolbar, le script focusout ne se lance pas), mais juste après, il se lance quand même. Eeeuh :shock:

Et du coup, c'est pour ça que quand je veux faire disparaître ma toolbar au focusout seulement, ça déconne.

Eléphant du PHP | 300 Messages

17 sept. 2019, 15:22

Je continue de parler tout seul :D
Il semblerait, si j'ai bien tout compris, que l'événement focusout s'exécute AVANT l'événement clic.
Donc, évidemment, ça expliquerait le comportement décrit dans le précédent message.