11540 sujets

JavaScript, DOM et API Web HTML5

Modérateur
Bonjour,


Je tente de colorer toutes les chaînes entourées d'accolades dans un texte contenu dans un div avec l'attribut contenteditable. Le code :

<div id="texte_editable" contenteditable onkeydown="colorTaggedText()" onkeyup="colorTaggedText()">
{test} pour voir si ça fonctionne on met du {texte_entre_accolades_sans_espaces} et on présente ça ici
</div>
<script type="text/javascript">
function colorTaggedText()
{
    var content = document.getElementById('texte_editable').innerHTML;
    var pattern = /({[^\s]+})/g;
    content = content.replace(pattern,'<span style="color: green">$1</span>');  
}
</script>


Ça ne fonctionne pas. J'ai vérifié, en plaçant un alert donnant la valeur de "content" à la fin de la fonction js et l'opération se fait bien, mais pas d'ajout du code généré dans la page Web.

Merci d'avance pour votre aide. Smiley smile
Modifié par jojaba (17 Feb 2013 - 22:57)
Salut, il faut que réédite le `innerHTML` de ton élément. C'est dû au fait que les strings sont passées par valeur et non par référence en javascript (et dans presque tous les langages d'ailleurs).

Passé par valeur: String, Number
Passé par référence: Object, Array, Function

function colorTaggedText()
{
    var textBox = document.getElementById('texte_editable');
    var content = textBox.innerHTML;
    var pattern = /({[^\s]+})/g;
    textBox.innerHTML = content.replace(pattern,'<span style="color: green">$1</span>');
    // Ou comme ceci plus simplement:
    // textBox.innerHTML.replace(pattern,'<span style="color: green">$1</span>');
}

Modifié par Felipe (24 Feb 2013 - 13:18)
Modérateur
Merci SBoudrias.

Alors sur la page que je souhaite mettre en production ça ne fonctionne pas. En revanche sur la page d etets que j'ai mis en place ça fonctionne effectivemment (attention, oubli d'un ; à la fin de la première ligne du script que tu as donné). Voici ma page de test qui fonctionne :
<!DOCTYPE html>
<html>
	<head>
	<title>Test contenteditable avec coloration js</title>
		<meta charset="utf-8" />
	</head>
	<body onload="colorTaggedText()">
		<h1>Test contenteditable avec coloration js</h1>
		
		<div id="texte_editable" contenteditable>
		Pour voir ce que ça donne. Je mets des {accolades} et j'en mets {une_deuxième_paire}<br />
		Deuxième ligne pour voir avec {les_accolades}.
		</div>
    	
		<script type="text/javascript">
		function colorTaggedText()
		{
			var textBox = document.getElementById('texte_editable');
    		var content = textBox.innerHTML;
    		var pattern = /({[^\s]+})/g;
    		textBox.innerHTML = content.replace(pattern,'<span style="color: green">$1</span>');
    	}
		</script>
	</body>
</html>


Je n'ai pas compris pourquoi ça ne marche pas sur ma page d'origine, mais on va continuer sur l'exemple ci-dessus...
Pour l'instant la coloration se fait au chargement de la page, mais j'aurais aimé que cela se passe également à chaque fois qu'une touche est appuyée afin que le div se mette à jour dynamiquement. L'évènement onkeypress aurait été bien s'il ne mettait pas le curseur au début du div. Je me doutais que cela allait posé problème... Il faudrait que je puisse récupérer la position du curseur lors de la frappe pour ensuite lancer la fonction de coloration puis revenir à nouveau la position initiale du curseur. Je sais que c'est possible mais je ne me souviens plus trop comment faire et j'ai du mal à trouver une ressource sur le Web à ce sujet...
Une piste ? Est-ce effectivement faisable ?

Merci.

**********edit*************
J'ai trouvé ça (pas testé, pas le temps là) :
if (window.ActiveXObject) { //For IE
			var textRange = window.document.selection.createRange();
			var currentSelection = textRange.text;
	} else { //For other browsers
			var startSelection   = field.value.substring(0, field.selectionStart);
			var currentSelection = field.value.substring(field.selectionStart, field.selectionEnd);
			var endSelection     = field.value.substring(field.selectionEnd);
	}

**********/edit*************
Modifié par Felipe (24 Feb 2013 - 13:17)
Modérateur
Bon, j'ai trouvé cette ressource-là :
http://www.cloudconnected.fr/2007/02/20/position-du-curseur-dans-un-textarea/
Qui explique comemnt repérer la position du curseur. Hélas, il semblerait que cela ne soit possible que dans un textarea...
J'en étais là pour l'instant (ça ne fonctionne pas mais le curseur ne revient pas au début du div pour le coup) :
<!DOCTYPE html>
<html>
	<head>
	<title>Test contenteditable avec coloration js</title>
		<meta charset="utf-8" />
	</head>
	<body onload="colorTaggedText()">
		<h1>Test contenteditable avec coloration js</h1>
		
		<div id="texte_editable" onfocus="colorTaggedTextFly()" onkeypress="colorTaggedTextFly()" onkeydown="colorTaggedTextFly()" onkeyup="colorTaggedTextFly()" contenteditable style="width: 90%; margin: 10px auto; padding: 5px; height: 200px; overflow: auto; border: #000 ridge 2px;">
		Pour voir ce que ça donne. Je mets des {accolades} et j'en mets {une_deuxième_paire}<br />
		Deuxième ligne pour voir avec {les_accolades}.
		</div>
    	
		<script type="text/javascript">
		function colorTaggedText()
		{
			
			var textBox = document.getElementById('texte_editable');
			var content = textBox.innerHTML;
			var pattern = /({[^\s]+})/g;
			textBox.innerHTML = content.replace(pattern,'<span style="color: green">$1</span>');
			
		}
		function colorTaggedTextFly()
		{
			var textBox  = window.document.getElementById('texte_editable');
			var scroll = textBox.scrollTop;
			textBox.focus();
			/* === Part 1: Getting the selection === */
			if (window.ActiveXObject)
			{ //For IE
				var textRange = window.document.selection.createRange();
				var currentSelection = textRange.text;
			} 
			else
			{ //For other browsers
				var startSelection   = textBox.value.substring(0, textBox.selectionStart);
				var currentSelection = textBox.value.substring(textBox.selectionStart, textBox.selectionEnd);
				var endSelection     = textBox.value.substring(textBox.selectionEnd);
			}
	    	/* === Part 2: Text coloration === */
			var content = textBox.innerHTML;
			var pattern = /({[^\s]+})/g;
			textBox.innerHTML = content.replace(pattern,'<span style="color: green">$1</span>');
			/* === Part 3: returning to the right place after treatment === */
			if (window.ActiveXObject)
			{ //For IE
				textRange.text = currentSelection;   
			}
			else
			{ //For other browsers
				textBox.value = startSelection + currentSelection + endSelection;
				textBox.focus();
			} 
			textBox.scrollTop = scroll;
		}
		</script>
	</body>
</html>
Et pourquoi un textarea est-il proscrit dans ton cas?

Qui plus est, comme tu compile sur le keypress, tu pourrais comparer la valeur précédente avec la nouvelle pour déterminer l'endroit où était positionné le curseur. On imagine évidemment un coût en performance, mais selon le contexte et les solutions technologiques disponibles, ce pourrait être une solution (si le texte n'est pas trop long, ou si tu utilise un webworker, etc).

**edit**

Je regardais l'implémentation de Dabblet, et l'app ne compile la couleur que lorsqu'une touche espace est pressé et entoure chaque mot dans un span. Alors, ce n'est pas le block d'édition en entier, mais chaque span (chaque chaine de caractère contigue) qui devient content-editable.

Dabblet est open source, alors tu peux jeter un oeil à leur implémentation.
Modifié par Felipe (24 Feb 2013 - 13:18)
Modérateur
Merci pour ton aide persistante Smiley smile
SBoudrias a écrit :
Et pourquoi un textarea est-il proscrit dans ton cas?

Coloration d'un fragment de texte dans un textarea, c'est pas possible à ma connaissance...
a écrit :

**edit**
Je regardais l'implémentation de Dabblet, et l'app ne compile la couleur que lorsqu'une touche espace est pressé et entoure chaque mot dans un span. Alors, ce n'est pas le block d'édition en entier, mais chaque span (chaque chaine de caractère contigue) qui devient content-editable.

Dabblet est open source, alors tu peux jeter un oeil à leur implémentation.

Wahou, le coup des span editables autour de chaque mot, c'est bien vu ça. Je vais effectivement jeter un oeil. Merci.
Modifié par Felipe (24 Feb 2013 - 13:18)
Modérateur
Bon finalement j'ai simplifié tout ça, je ne suis vraiment pas à l'aise avec JS et je préfère mettre en place quelque chose de simple et que je comprends. La coloration se fera donc au chargemet de la page ou au clic sur un bouton. Voici ce que ça donne :
<!DOCTYPE html>
<html>
	<head>
	<title>Test contenteditable avec coloration js</title>
		<meta charset="utf-8" />
	</head>
	<body onload="colorTaggedText()">
		<h1>Test contenteditable avec coloration js</h1>
		<a href="#" onclick="colorTaggedText(); return false;">Coloration</a><br />
		<div id="texte_editable" contenteditable style="width: 90%; margin: 10px auto; padding: 5px; height: 200px; overflow: auto; border: #000 ridge 2px;">
		Pour voir ce que ça donne. Je mets des {accolades} et j'en mets {une_deuxième_paire}<br />
		Deuxième ligne pour voir avec {les_accolades}.
		</div>
    	
		<script type="text/javascript">
		function colorTaggedText()
		{
			var textBox = document.getElementById('texte_editable');
			var content = textBox.innerHTML;
			// Coloration des chaînes entre accolades
			var pattern = /([^>])(\{[^\s]+\})/g;
			textBox.innerHTML = content.replace(pattern,'$1<span style="color: green">$2</span>');
			// Suppression de la coloration si plus d'accolades
			content = textBox.innerHTML;
			var rbpattern = /(<span style="color: green">)([^{]{1})/g;
			textBox.innerHTML = content.replace(rbpattern,'$2');
			content = textBox.innerHTML;
			var rapattern = /([^}]{1})(<\/span>)/g;
			textBox.innerHTML = content.replace(rapattern,'$1');
		}
		</script>
	</body>
</html>

Pour éviter les double, triple, quadruple,... insertions de span lors du clic sur le bouton de coloration, j'ai modifié la regexp de coloration (on repère si la première accolade est précédée par un >). Pour supprimer la coloration en cas de modification des chaînes mises en évidence (donc entre accolades avant modification), j'ai ajouté deux regexp qui localisent les chaînes colorés sans accolades.
Modifié par jojaba (19 Feb 2013 - 23:33)
Modérateur
SBoudrias a écrit :
Ce n'est pas de la coloration syntaxique, mais je présume que tu veux créer un éditeur quelconque... Alors ce plugin pourrait peut-être t'aider: http://jakiestfu.github.com/Behave.js/

Ah ben je connaissais pas, ça a l'air relativement simple à prendre en main, merci. Pour la coloration syntaxique à la volée, je m'étais à un moment intéressé à CodeMirror, mais ce n'est pas la peine de sortir la grosse artillerie pour mes besoins actuels (surtout que je ne suis pas sûr qu'on puisse l'adapter à ce qui me serait utile).
Pour préciser ce que je souhaite faire, je propose un textarea dans lequel s'affiche un modèle de message (envoyé à des utilisteurs) qui contient des éléments (que je mets donc entre accolades) qui devront être modifiés par le client (l'administrateur du site). Pour faciliter la recherche de ces parties variables, j'aurais aimé pouvoir les mettre en évidence d'une manière ou d'une autre (la coloration me semblait être le plus approprié).
Modifié par Felipe (24 Feb 2013 - 13:19)
Modérateur
Eh bien finalement j'ai tout de même opté pour CodeMirror...
Voici la page de test :
<!DOCTYPE html>
<html>
	<head>
	<title>Textarea avec coloration js</title>
		<meta charset="utf-8" />
		<script src="codemirror/lib/codemirror.js"></script>
		<link rel="stylesheet" href="codemirror/lib/codemirror.css">
		<style>
		.CodeMirror { height: 300px; width: 750px; margin: 10px auto; padding: 5px; border: 2px solid #000;}
		.cm-mustache { color: green; }
		</style>
	</head>
	<body>
		<h1>Test textarea avec coloration CodeMirror</h1>
		
		<textarea id="texte">Pour voir ce que ça donne. Je mets des {{accolades}} et j'en mets {{une_deuxième_paire}} Deuxième ligne pour voir avec {{les_accolades}}.</textarea>

		<script>
		CodeMirror.defineMode("mustache", function() {
			return {
				token: function(stream, state) {
					var ch;
					if (stream.match("{{")) {
						while ((ch = stream.next()) != null)
						if (ch == "}" && stream.next() == "}") break;
						stream.eat("}");
						return "mustache";
					}
					while (stream.next() != null && !stream.match("{{", false)) {}
					return null;
				}
			};
		});
		var editor = CodeMirror.fromTextArea(document.getElementById("texte"), {
			mode: "mustache",
			lineWrapping: true,
			});	
		</script>
	</body>
</html>

Je ne me suis pas embêté, je me suis inspiré de cet exemple : http://codemirror.net/demo/mustache.html
Il ne reste plus qu'à savoir comment récupérer le contenu de l'editeur qui remplace le textarea et l'envoyer vers le textarea avant que le conteu de celui-ci soit envoyé par la méthode post...
Modifié par jojaba (21 Feb 2013 - 20:45)