11548 sujets

JavaScript, DOM et API Web HTML5

Bonjour,

Comme beaucoup j'essaie (en vain) de réaliser un petit WYSIWYG simple et qui produit du code correct.
J'ai essayé beaucoup de méthodes qui ne me conviennent vraiment pas (comme l'execCommand par exemple qui ne produit pas la même chose sur tous les navigateurs...)

Je ne suis pas expert en javascript, mais j'aime bien bricoler avec, du coup:
à partir d'un iframe ou d'une div j'aimerai faire une sélection, que je puisse récupérer cette sélection et la remplacer par un code de mon choix.
Ce que j'arrive très bien à faire dans un textarea et que je n'arrive pas à transposer pour une div ou iframe...

Pour mon textarea j'ai ceci et ça marche très bien:

<script type="text/javascript">
//<![CDATA[

function formatText(Texte,tag){
var selectedText = document.selection?document.selection.createRange().text:Texte.value.substring(Texte.selectionStart,Texte.selectionEnd);// IE:Moz
if(tag=='b') {var newText= "<strong>" + selectedText + "</strong>";}
if(document.selection){document.selection.createRange().text=newText;}//IE
else{Texte.value=Texte.value.substring(0,Texte.selectionStart)+newText+Texte.value.substring(Texte.selectionEnd,Texte.value.length);}
}//Moz

//]]>
</script>
<input type="button" value="Gras"  onclick="formatText(content,'b');" />
<textarea  name="content" id="content" ></textarea>



Du coup pour réaliser la même chose dans un div ou iframe je galère:


<script type="text/javascript">
 
 function getSelectedText(){
 if (window.getSelection){
 var str = window.getSelection();
 }else if (document.getSelection){
 var str = document.getSelection();
 }else {
 var str = document.selection.createRange().text;
 }
 return str;
 }
 function affichage(){
 var sel = getSelectedText();
 alert(sel);
 }
 </script>
 </head>
 <body>
 <div id="lala">Du texte à sélectionner dans la page</div>
 <p><input type="button" onClick="affichage()" value="Afficher la sélection" /></p>


Ce qui me permet donc de récupérer la sélection, je sui sallé plus loin en permettant un remplacement de la chaine dans le div, mais ça me remplace pas cette chaine précisément mais celle qui est rencontré en premier dans le div.

Ex: Admettons que j'ai ce texte dans une div: Bonjour je suis content!
Et que je veuille mettre en gras le "j" de "je", dans ce que j'ai réalisé il me remplace le premier "j" qu'il rencontre c'est à dire le "j" de "Bonjour"...
C'est pourquoi je reviens au code ci-dessus récupérant la sélection et j'aimerai récupérer via celle-ci le début de la sélection, sa longueur et la fin de la sélection... pour réinjecter une chaine remplaçante au même endroit...

Quelqu'un pourrait-il m'aider?
Bonjour,

Actuellement je boss sur mon propre richtext edit et essai de la rendre compatible Mozilla, la première chose que j'ai remarqué c'est que document.selection.createRange().text ne passe pas avec Mozilla il faut utiliser window.getSelection().

ensuite j'ai remarqué que cette méthode marche très bien mais que les sélections faites dans un div sont pas prise en compte par cette fonction, encore une difficulté sous Mozilla.

j'aimerai vraiment savoir si tu as réussi à trouver une méthode et comment.

philippe
Pour tout te dire j'ai galéré longtemps... et puis je suis passé à autre chose, surtout après avoir trouvé mon bonheur (presque) sur la page suivante:
http://www.themaninblue.com/experiment/widgEditor/

Il y a encore quelques améliorations à faire, mais ça me plait bien.
Je l'ai donc récupéré et après quelques modifications visuelles (oui, là ça me paraissait important!), j'ai un truc sympa pour bosser.
Salut,

Voici un article avec 8 solutions de wysiwig, peut être vous n'avez pas testé ces solutions dont certaines me semblent plus connues que d'autres (tinyMCE etc..).
C'est possible, j'en ai fait un, 100% maison, puisqu'il n'utilise pas la fonction ExecCommand. Ses possibilités vont bien au-delà des éditeurs standards, mais il reste pas mal de travail à faire dessus.

Pour en voir les effets, allez sur http://libriolounge.fr/dossiers.php, puis visualisez un billet, et cliquez sur les références de bouquin en rouge : un onglet sort qui contient la référence complète, un lien pour consulter la fiche complète, et un lien partenaire.

Ceci nécessite de pouvoir faire insérer une arborescence de balises directement dans le texte, et sans que l'utilisateur n'ait une quelconque connaissance technique du web. L'utilisatrice, en l'occurrence, est néophyte, ce qui n'empêche qu'elle peut ajouter ce genre de références en deux clics, et en lien avec sa base de données.
La solution la meilleure (selon moi), c'est de récupérer les noeuds de départ et d'arrivée de la sélection, ainsi que les index de départ et de fin : par exemple :
<div>Bonjour <span>monde !</span></div>

Admettons qu'on ait sélectionné le texte "our mon", ce qui est utile, c'est de savoir que la sélection commence dans le premier fils de la DIV, à l'index 4, et finit dans le premier fils du SPAN à l'index 3.

De là, tout est faisable, c'est que du DOM, ou plutôt de la mise en relation d'un texte (linéaire), avec un DOM (arborescent (ou arboricole Smiley cligne ).

La difficulté vient d'IE avec qui cette relation n'est pas évidente, car il ne dispose que de peu de méthodes permettant de trouver des éléments du DOM à partir d'un range.
Bonjour

Excusez moi de remonter ce topic, mais je me pose des questions quand à la faisabilité d'un Wysiwyg personnel.

ça me paraît être une solution très lourde à gérer. Le contexte doit donc être défini très précisément :

- La liste des navigateurs supportés : Plus les navigateurs supportés sont nombreux, plus les fonctionnalités seront limitées. Pour ma part, j'ai déjà complètement abandonné le support d'IE pour ce qui concerne l'édition / gestion de contenu. Je reprendrais le support après la sortie de la version 9.

- La remarque du post précédente est très juste. Il faut utiliser le noeud parent afin de prendre en compte son style et d'appliquer les nouveaux paramètres de style en conséquence.

- Gérez-vous les styles issus des copier / coller ? Je me rends compte de mes clients préfèrent souvent utiliser Word. Il me paraît donc indispensable d'assurer la compatibilité.

A mon avis, gérer la mise en forme à partir des saisies clavier est quelque chose de faisable. Par contre dès qu'il s'agit de gérer du code déjà mis en forme par un logiciel tiers, c'est autrement plus complexe.
Je vois mal comment cela pourrait être rentable en terme de temps de développement de chercher à coder complètement un Wysiwyg plutôt que d'en intégrer un tout fait (quitte à le payer quelques centaines d'euros).

Je suis curieux d'en savoir plus sur vos investissements (en temps, en euros) ainsi que l'utilisation de code déjà produit (classe Javascript ? Gratuites ? Payantes ?)

Pour ma part, je suis à la recherche d'un Wysiwyg qui puisse prendre en compte et modifier du contenu depuis une balise Div et non à partir d'un textarea.
Je serais encore plus intéressé par une classe Javascript avec des méthodes du type
mettreEnGrasLaSelection();
changerCouleurTexte("#FF7654");

Bien le bonjour chez vous
gaboul49 a écrit :
- La liste des navigateurs supportés : Plus les navigateurs supportés sont nombreux, plus les fonctionnalités seront limitées. Pour ma part, j'ai déjà complètement abandonné le support d'IE pour ce qui concerne l'édition / gestion de contenu. Je reprendrais le support après la sortie de la version 9.


Pas du tout. WYMeditor, par exemple, fonctionne sur tous les navigateurs à partir d'IE6. Smiley smile

gaboul49 a écrit :
- Gérez-vous les styles issus des copier / coller ? Je me rends compte de mes clients préfèrent souvent utiliser Word. Il me paraît donc indispensable d'assurer la compatibilité.


En général les attribut de mise-en-forme sont convertit lors du copier-coller. Si l'éditeur est bien fait, il fera même un nettoyage du code.

gaboul49 a écrit :
A mon avis, gérer la mise en forme à partir des saisies clavier est quelque chose de faisable. Par contre dès qu'il s'agit de gérer du code déjà mis en forme par un logiciel tiers, c'est autrement plus complexe.
Je vois mal comment cela pourrait être rentable en terme de temps de développement de chercher à coder complètement un Wysiwyg plutôt que d'en intégrer un tout fait (quitte à le payer quelques centaines d'euros).

Je suis curieux d'en savoir plus sur vos investissements (en temps, en euros) ainsi que l'utilisation de code déjà produit (classe Javascript ? Gratuites ? Payantes ?)

Pour ma part, je suis à la recherche d'un Wysiwyg qui puisse prendre en compte et modifier du contenu depuis une balise Div et non à partir d'un textarea.
Je serais encore plus intéressé par une classe Javascript avec des méthodes du type
mettreEnGrasLaSelection();
changerCouleurTexte("#FF7654");

Bien le bonjour chez vous


Si le site est bien foutu, il n'est plus nécessaire (sauf exception) d'avoir accès à la mise en couleur. On utilise les "styles" standards : h1, h2, etc plus certaines classes dans des cas précis. WYMeditor fait ça et il est gratuit/open source. Smiley smile
À l'heure actuelle, se lancer dans la création d'un éditeur WYSIWYG me semble une perte de temps, à moins d'être un expert du sujet et de vouloir lancer un projet conséquent (open source ou commercial), en sachant précisément ce que l'on pourra apporter de différent.

Si les éditeurs actuels ne donnent pas satisfaction, c'est en gros pour trois raisons, par ordre croissant d'importance:
1. Les fonctionnalités d'édition des navigateurs sont foireuses à la base, et ces outils font ce qu'ils peuvent.
2. On n'a pas pris la peine d'apprendre à les utiliser, d'exploiter à fond leur paramètres voire leur API.
3. On cherche à régler avec un outil ce qui relève en partie de la formation de rédacteurs et correcteurs web, et en partie du développement (par architectes de l'information + ergonomes + développeurs front + développeurs back) de solutions ad hoc de saisie de contenus typés.

Pour ma part je trouve les outils disponibles de bonne qualité. Les trois que je retiens sont:
- Mark It Up pour une aide au formatage de textes avec marquage syntaxique (HTML brut, Markdown, BBcode, syntaxes wiki...).
- WYMEditor pour du WYSIWYM (moyennant une refonte ergonomique de ses contrôles).
- CKEditor pour du WYSIWYG (moyennant une configuration poussée).

<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<title>Mon WYSIWYG</title>

<style type="text/css">
body{
height:auto;
width:440px;
text-align:justify;
line-height:1.5em;
}
.gras{
font-weight:bold;
}
.italique{
font-style:italic;
}
q{
text-decoration:underline;
color:#4c69b0;
}
q:Before {
Content:'';
}
q:After {
Content:'';
}
.ref{
text-decoration:underline;
color:#4c69b0;
}
.para{
width:100%;
left:0px;
display:block;
margin-top:10px;
margin-bottom:10px;
margin-left:0px;
padding-left:10px;
text-indent:10px;
}
.onglet_ref{
width:100%;
background-color:#4c69b0;
color:#e6e6fa;
border:1px solid #e6e6fa;
position:absolute;
bottom:0px;
left:0px;
z-index:500;
}
.cont_ref{
width:100%;
height:auto;
background-color:#4c69b0;
color:#e6e6fa;
border:1px solid #e6e6fa;
z-index:500;
position:absolute;
left:0px;
top:0px;
}
.bloc_citation{
height:0px;
width:100%;
min-width:100px;
display:inline-block;
position:absolute;
bottom:0px;
left:0px;
}
</style>

<SCRIPT>
var id_cadre='faux_textarea';
var ie=false;
if(navigator.appName=="Microsoft Internet Explorer")ie=true;
if(navigator.appName!="Microsoft Internet Explorer")field=window;

function define_noParse(classe,style){
	this.classe=classe;
	this.style=style.replace(/(\s)/g,"");
}

tab_noParse=new Array();

tab_noParse["span"]=new define_noParse('',"display:none");

function Range(){

	this.elemStart=null;
	this.elemEnd=null;
	this.offsetStart=null;
	this.offsetEnd=null;
	if(!ie)var Field=field;
	if(ie)var Field=null;
	this.Range=null;
	
	//privée : détermine le range brut d'après une sélection utilisateur
	var getRange=function(){
		if(ie){
			range = document.selection.createRange();
		}else{
			var selection=Field.getSelection();
			range = selection.getRangeAt(0);
		}
		return range;
	}
	//publique : permet de récuprer le range "nettoyé" c-a-d placé intelligemment par rapport au DOM
	this.cleanRange=function(){
		var range=getRange();
		if(ie){
			var parent=range.parentElement();
			var range_2=range.duplicate();
			range_2.moveToElementText(parent);
			range_2.collapse(true);
			var count=0;
			var elem_courant=parent.firstChild;
			while(range_2.compareEndPoints('StartToStart',range)<0){
				range_2.move('character',1);
				count++;
				if(count>=elem_courant.nodeValue.length){
					elem_courant=firstTextNode(elem_courant,true);
					count=0;
				}
			}
			
			this.ElemStart=elem_courant;
			this.offsetStart=count;
			
			var parent=range.parentElement();
			var range_2=range.duplicate();
			range_2.moveToElementText(parent);
			range_2.collapse(false);
			var count=0;
			var elem_courant=parent.lastChild;
			while(range_2.compareEndPoints('EndToEnd',range)>0){
				range_2.move('character',-1);
				count++;
				if(count>=elem_courant.nodeValue.length){
					elem_courant=firstTextNode(elem_courant,false);
					count=0;
				}
			}
			this.ElemEnd=elem_courant;
			this.offsetEnd=elem_courant.nodeValue.length-count;
		}else{
			this.ElemStart=range.startContainer;
			this.offsetStart=range.startOffset;
			if(this.ElemStart.nodeType!=3 || this.offsetStart==this.ElemStart.nodeValue.length){
				var elem_courant=this.ElemStart;
				if(elem_courant.childNodes[this.offsetStart] && elem_courant.childNodes[this.offsetStart].nodeType==3){
					range.setStart(elem_courant.childNodes[this.offsetStart],0);
					this.ElemStart=elem_courant.childNodes[this.offsetStart];
					this.offsetStart=0;
				}else{
					elem_courant=firstTextNode(elem_courant,true);
					range.setStart(elem_courant,0);
					this.ElemStart=elem_courant;
					this.offsetStart=0;
				}
			}
			this.ElemEnd=range.endContainer;
			this.offsetEnd=range.endOffset;
			if(this.ElemEnd.nodeType!=3 || this.offsetEnd==0){
				var elem_courant=this.ElemEnd;
				if(elem_courant.childNodes[this.offsetEnd] && elem_courant.childNodes[this.offsetEnd].nodeType==3){
					range.setEnd(elem_courant.childNodes[this.offsetEnd],elem_courant.childNodes[this.offsetEnd].nodeValue.length);
					this.ElemEnd=elem_courant.childNodes[this.offsetEnd];
					this.offsetEnd=elem_courant.childNodes[this.offsetEnd].nodeValue.length;
				}else{
					elem_courant=firstTextNode(elem_courant,false);
					range.setEnd(elem_courant,elem_courant.nodeValue.length);
					this.ElemEnd=elem_courant;
					this.offsetEnd=elem_courant.nodeValue.length;
				}
			}
		}
		this.Range=range;
	}
	
	function firstTextNode(node,dir){
		//fonction chargée de trouver le noeud texte le plus proche du noeud passé en argument, dans la direction avant (dir=true) ou arrière (dir=false)
		if(dir){
			do{
				if(node.firstChild || node.nextSibling){
					node=node.firstChild || node.nextSibling;
				}else{
					while(!node.nextSibling)node=node.parentNode;
					node=node.nextSibling;
				}
			}while(node.nodeType!=3)
		}else{
			do{
				if(node.lastChild || node.previousSibling){
					node=node.lastChild || node.previousSibling;
				}else{
					while(!node.previousSibling)node=node.parentNode;
					node=node.previousSibling;
				}
			}while(node.nodeType!=3)
		}
		return node;
	}

	return this.Range;
}

function test_objet(){
	var select=new Range();
	var MyRange=select.cleanRange();
	alert(select.ElemStart.nodeValue+'//'+select.offsetStart);
	alert(select.ElemEnd.nodeValue+'//'+select.offsetEnd);
}
</SCRIPT>
</head>
<body><input type="button" value="lancer !" onclick="test_objet()" /><span class="ref">abblabla<span class="gras" >cd<span class="italique">ef<span style="display:none">blablabla</span>gh</span>ij</span>kl</span></body>
</html>


Désolé pour la syntaxe, les variables mal dégrossies, tout le bouzin qui offusquera les développeurs. J'ai pas eu le temps de travailler davantage ce bout de code, qui était un test suite à mon premier post.

L'idée générale, c'est de récupérer le noeud xHTML et la position dans ce noeud du début et de la fin de la sélection.

Il faut savoir que pour appliquer cela à un WYSIWYG, Mozilla & Co vont utiliser une Iframe pour faire un cadre au contenu éditable, alors que IE va utiliser une DIV. Dans les deux cas on aura ajouté un attribut à la balise Iframe ou DIV, pour Mozilla, contentEditable = true.

Donc, si vous sélectionnez du texte de test dans la page et que vous cliquez sur lancer!, deux alert vous donneront le contenu du noeud et le numéro de la lettre où commence la sélection, puis idem pour la fin de la sélection.

Ca mérite encore bien du travail, j'aurais l'occasion de "développer" cette solution durant les mois qui viennent, alors dites-moi si c'est un bon début....ou pas.

Je le répète, c'est un test pondu entre deux contrats, donc l'idée est là, ça marche, mais le code n'est même pas nettoyé, c'est une ébauche.