Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Наши скрипты > Как вставить текст под курсором


Автор: Sardar 27.11.2004, 18:58
 Вопрос: Как вставить текст под курсором.

Столкнулся сегодня с этой задачей, решил ответить на него один раз с рабочим кодом. Надеюсь больше этот вопрос не всплывёт smile

В разных браузерах по разному можно узнать где находится каретка в текстовом поле. Точнее только в Мозилле на данный момент есть возможность узнать положение каретки опрашивая поля:
  • selectionStart -  начало селекции, в символах
  • selectionEnd - конец селекции, в символах
Таким образом можно разбить содержимое на подстроки: до селекции, селекция, после селекции.

В ИЕ есть полезный обьект document.selection который может достать нам обьект DOM'овский обьект(ну почти DOM'овский smile ) TextRange. Изменяя этот обьект мы изменяем содержимое селекции.
Селекция здесь в самом широком смысле! То есть мы можем таким же образом работать над всем содержимым страницы.

В Опере код работать не будет, по старинке текст добавляется в конец.

Следующая сложность это не потерять селекцию(положение курсора) при клике на внешний элемент. Будем отлавливать события потери фокуса, клика и т.д., записывать "позицию курсора" и использовать её когда нужно.
В моём коде есть потенциальная ошибка: если отселектировать текст, затем кликнуть где нибудь по странице, а затем по кнопке вставляющей новый текст, то он будет вставлен в записанную позицию, что не совсем верно - селекция должна исчезнуть если кликаем не по кнопке. Решается отловом событий на body и сбросом записанной селекции, но это по вкусу smile

Код с коментариями:
Код
/**
* Обьект через который мы и будем работать с текстовыми полями.
* Содержит все необходимые поля, легко расширяем по вкусу =)
* @argument  obj  HTMLElment  - текстовое поле, с кторым предстоит работать
* 
* Author: Sardar <[email protected]>
*/
function TextAreaSelectionHelper(obj) {
  this.target=obj;
  this.target.carretHandler=this; //ссылка самого на себя для текстового поля
  /**
  * Помним, что события могут быть уже опредлеенны, тогда нужно использовать
  * очереди. Конечно, реализация для ИЕ и Мозиллы в корне различаются, почитать
  * и достать готовый код можно здесь: http://forum.vingrad.ru/index.php?showtopic=32350
  */
  this.target.onchange=_textareaSaver;
  this.target.onclick=_textareaSaver;
  this.target.onkeyup=_textareaSaver;
  this.target.onfocus=_textareaSaver;
  if(!document.selection) this.target.onSelect=_textareaSaver; //для Мозиллы
  
  this.start=-1;
  this.end=-1;
  this.scroll=-1;
  this.iesel=null; //для ИЕ
}
/**
* Достать отселектированный текст
*/
TextAreaSelectionHelper.prototype.getSelectedText=function() {
   return this.iesel? this.iesel.text: (this.start>=0&&this.end>this.start)? this.target.value.substring(this.start,this.end): "";
}
/**
* Вставить код под курсором. Если текст не отселектирован(не фокуса) и 
* позиция не взята, то вставить в конец текстового поля.
*
* @argument text String - заменить селекцию на этот текст
* @argument secondtag String - если задан, то селекция не заменяется, а обрамляется этими тегами
*/
TextAreaSelectionHelper.prototype.setSelectedText=function(text, secondtag) {
  if(this.iesel) {
    if(typeof(secondtag)=="string") {
   var l=this.iesel.text.length;
      this.iesel.text=text+this.iesel.text+secondtag;
   this.iesel.moveEnd("character", -secondtag.length);
    this.iesel.moveStart("character", -l);   
    } else {
   this.iesel.text=text;
    }
    this.iesel.select();
  } else if(this.start>=0&&this.end>=this.start) {
     var left=this.target.value.substring(0,this.start);
     var right=this.target.value.substr(this.end);
  var scont=this.target.value.substring(this.start, this.end);
  if(typeof(secondtag)=="string") {
    this.target.value=left+text+scont+secondtag+right;
    this.end=this.target.selectionEnd=this.start+text.length+scont.length;
    this.start=this.target.selectionStart=this.start+text.length;    
  } else {
       this.target.value=left+text+right;
    this.end=this.target.selectionEnd=this.start+text.length;
    this.start=this.target.selectionStart=this.start+text.length;
  }
  this.target.scrollTop=this.scroll;
  this.target.focus();
  } else {
    this.target.value+=text + ((typeof(secondtag)=="string")? secondtag: "");
    if(this.scroll>=0) this.target.scrollTop=this.scroll;
  }
}
/**
* Это функции для веса =)
*/
TextAreaSelectionHelper.prototype.getText=function() {
  return this.target.value;
}
TextAreaSelectionHelper.prototype.setText=function(text) {
  this.target.value=text;
}
/**
* Приватная фукнкция, записывающая позицию курсора
*/
function _textareaSaver() {
  if(document.selection) {
    this.carretHandler.iesel = document.selection.createRange().duplicate();
  } else if(typeof(this.selectionStart)!="undefined") {
    this.carretHandler.start=this.selectionStart;
    this.carretHandler.end=this.selectionEnd;
    this.carretHandler.scroll=this.scrollTop;
  } else {this.carretHandler.start=this.carretHandler.end=-1;}
}

..........
//Пример использования
//эта строка отработает после загрузки страницы.
onload=function(){text=new TextAreaSelectionHelper(document.getElementById("textarea"));}

function test() {
 text.setSelectedText("[test]","[/test]");
// text.setSelectedText("[test]");
}

.......
<textarea id="textarea" cols=80 rows=20></textarea>
<span onClick="test()">Test</span>
 

Автор: Alx 28.11.2004, 01:46
аааааааа
Sardar ты монстр! господи, я и не представлял, что эти чёртовы разные браузеры могут делать из одной простой функции в ИЕ такую туеву хучу всякого кода! smile

Автор: Sardar 28.11.2004, 02:23
Кода на самом деле не много(подсветка убила форматирование), просто возможности чуть шире smile
Забываем о текстовом поле, создаем обьект и работаем с созданным обьектом.
Смотри пример, запусти.

Автор: Black 17.12.2004, 20:57
ALEXANDRO
где Сардар подвел итог я не нашел зато сделал что хотел :-)

набо было то всего:


Код

<script>
// определяем позицию курсора
var xxx = parent.frames['xxx'].document.forms["xxx"].elements['xxx'];
selStart=xxx.selectionStart;
selEnd=xxx.selectionEnd;
scrTop=xxx.scrollTop;
// дальше некоторый код и затем
// ставим курсор после кода смайлика
    xxx.selectionStart=selStart +code.length;
    xxx.selectionEnd= xxx.selectionStart;
    xxx.scrollTop= scrTop;
    parent.frames['xxx'].document.forms['xxx'].elements['xxx'].focus(); //ставим курсор
</script>


вот что я хотел smile

Автор: Sardar 18.12.2004, 01:03
PHP-DIRECTOR если не понял, значит удалять ничего не буду...

Поделим мир на 3 части:
  • те кто пользуется Opera - безнадёжные парни, им никогда не увидеть сей радости
  • те кто пользуется Мозиллоподобными - используется немного примитивный подход, мы задаём для текстового поля начало и конец селекции. Курсор это тоже селекция, где начало и конец в одной точке.
  • те кто пользуется ИЕ - используется немного навороченный способ, управление через обьект selection.
Как видим реально узнать позицию курсора можно только в Мозилле. В IE можно отрасировать текст перед селекцией и узнать позицию, но нам это не нужно, мы просто вставляем в селекцию текст, который и встанет на место курсора.

Также нужно запоминать позицию курсора при потере фокуса, ведь если ты кликнешь на кнопку, то фокус с текстового поля уйдёт smile

Разбирай мой код, там сразу станет ясно smile
Как его использовать:
Код
//пусть у нас есть некоторое текстовое поле
<textarea id="my_text_pole"></textarea>
.....
var txtpole=document.getElementyById("my_text_pole");
//воспользуемся моимкодом, что бу просто работать с этим полем
var hTextarea = new TextAreaSelectionHelper(txtpole);

//допустим этой фунцкией мы будем вставлять смайлики(что угодно) под курсором
function vstav_smajl_pod_kursorom(smile) {
hTextarea.setSelectedText(smile);
}
//а вот этой фунцкией мы будем оборачивать отселектированный текст в заданные теги
function oberni_v_teg_pod_kursorom(tagOpen, tagClose) {
 hTextarea.setSelectedText(tagOpen, tagClose);
}

//как видишь всё ясно :)

<span onclick="vstav_smajl_pod_kursorom(':)')">:)</span><br>
<span onclick="oberni_v_teg_pod_kursorom('[sometag]', '[/sometag]')">Обернуть в [sometag]</span>

Автор: Aliance 24.3.2005, 01:16
Sardar
Огромное спасибо за данный код, это просто бестцеллер :-)
6 с половиной баллов smile

Автор: Sardar 24.3.2005, 01:20
Aliance на логе по аське можно создать небольшой мануал, а то вижу народу не ясно smile
Скинь ссылки на примеры что наверстали, будет интересно. А вообще надо смотреть исходник в новом дизайне, там многие фичи включая эту используются. Ждём когда его введут smile

Автор: Aliance 24.3.2005, 03:14
Не хотел радовать преждевременно, но завтра (точнее уже сегодня, но после ночи) я как раз собирался выложить готовый пример, с комментариями. без лишнего мусора.

Автор: sergejzr 24.3.2005, 03:28
Новая Опера включена?

Автор: Sardar 24.3.2005, 03:48
Опера вроде бы обьект selection поддерживает, следовательно должно работать, если они от поведения ИЕ не отклонялись. В любом случае надо потестить.

Автор: Aliance 24.3.2005, 12:20
Пример тут: http://files.vingrad.ru/Aliance/bbcode.html

Автор: sergejzr 24.3.2005, 13:32
Блин! Эта Опера опять выделывается...

Код

http://files.vingrad.ru/Aliance/bbcode.html
Event thread: click
Error:
name: TypeError
message: Statement on line 107: object required
Backtrace:
  Line 107 of inline#1 script in http://files.vingrad.ru/Aliance/bbcode.html
    if (this.iesel && this.iesel.parentElement() == this.target)
    else
      Line 209 of inline#1 script in http://files.vingrad.ru/Aliance/bbcode.html
    post.setSelectedText(tag1, tag2);
  Line 1 of  script 
    wrapTags("[b]", "[/b]");
  At unknown location
    [statement source code not available]

У меня к сож времени нету разбираться smile

Автор: Sardar 24.3.2005, 17:55
Очень странно, как будто TextRange.parentElement не поддерживается... Поставлю у себя этого зверя, пофиксю, в новом скине этот код применяется... ведь обещали нормальную поддержку... На opera.com последняай документация на версию 7, о 8 только реклама...

Автор: sergejzr 31.3.2005, 03:22
Я перенял твой код и у меня с ним Опера работает... smile
http://www.iwi-iuk.org:8081/tex/index.php


Автор: Aliance 1.4.2005, 18:42
А функция «Быстрой цитаты» работает по этому же коду/принципу?

Автор: sergejzr 1.4.2005, 18:52
С ие и мозиллой он всегда работал. проблема только с Оперой
Цитата(Aliance @ 1.4.2005, 17:42)
А функция «Быстрой цитаты» работает по этому же коду/принципу?

Нет, когда сделали сперва, то всем ужастно не понравилось, что цитата в курсор вставлялась. Было очень неудобно.

Добавлено @ 18:57
http://forum.vingrad.ru/index.php?showtopic=32152&hl=
Где то здесь обсуждали smile

Автор: Aliance 1.4.2005, 19:09
Цитата(sergej @ 1.4.2005, 18:52)
когда сделали сперва

А как тогда сейчас? Впринципе, различие там только в том, чтобы только отселектрированный текст вставлять...

Автор: sergejzr 1.4.2005, 19:11
Value+=цитата smile

Автор: Sardar 1.4.2005, 20:41
sergej.z, а я цитату в новом дизайне под курсором вставляю, нужно исправить...

Автор: Aliance 1.4.2005, 21:07
Цитата(sergej @ 1.4.2005, 19:11)
Value+=цитата

А чего так? =/

Автор: sergejzr 1.4.2005, 21:10
Цитата(Aliance @ 1.4.2005, 20:07)
А чего так? =/

Вот, нашёл smile
http://forum.vingrad.ru/index.php?showtopic=29258&unread=1&hl=
Цитата(Sardar @ 1.4.2005, 19:41)
sergej.z, а я цитату в новом дизайне под курсором вставляю, нужно исправить...

Как видишь...

Автор: Wolf1994 1.8.2005, 15:17
Что-то я не смог разобраться с установкой примера, предложенного Sardar'ом, под IE 6. В JavaScript я разбираюсь плохо, поэтому не смог проанализировать код. Как мне кажется, не записывается позиция курсора - вставка у меня осуществляется в конец текста. Возможно я что-то не так сделал, тогда, пожалуйста, подскажите - что?

Файл HTML:
Код

<script src="insert.js"></script>
<textarea id="textarea1" cols=80 rows=20>hhj</textarea>
<span onClick="test()">Test</span>


Файл "insert.js":
Код

/**
* Обьект через который мы и будем работать с текстовыми полями.
* Содержит все необходимые поля, легко расширяем по вкусу =)
* @argument  obj  HTMLElment  - текстовое поле с кторым предстоит работать
* 
* Author: Sardar <[email protected]>
*/
function TextAreaSelectionHelper(obj) {
 this.target=obj;
 this.target.carretHandler=this; //ссылка самого на себя для текстового поля
 /**
 * Помним что события могут быть уже опредлеенны, тогда нужно использовать
 * очереди. Конечно реализация для ИЕ и Мозиллы в корне различаются, почитать
 * и достать готовый код можно здесь: http://forum.vingrad.ru/index.php?showtopic=32350
 */
 this.target.onchange=_textareaSaver;
 this.target.onclick=_textareaSaver;
 this.target.onkeyup=_textareaSaver;
 this.target.onfocus=_textareaSaver;
 if(!document.selection) this.target.onSelect=_textareaSaver; //для Мозиллы
 
 this.start=-1;
 this.end=-1;
 this.scroll=-1;
 this.iesel=null; //для ИЕ
}
/**
* Достать отселектированный текст
*/
TextAreaSelectionHelper.prototype.getSelectedText=function() {
  return this.iesel? this.iesel.text: (this.start>=0&&this.end>this.start)? this.target.value.substring(this.start,this.end): "";
}
/**
* Вставить код под курсором. Если текст не отселектирован(не фокуса) и 
* позиция не взята, то вставить в конец текстового поля.
*
* @argument text String - заменить селекцию на этот текст
* @argument secondtag String - если задан, то селекция не заменяется, а обрамляется этими тегами
*/
TextAreaSelectionHelper.prototype.setSelectedText=function(text, secondtag) {
 if(this.iesel) {
if(typeof(secondtag)=="string") {
  var l=this.iesel.text.length;
     this.iesel.text=text+this.iesel.text+secondtag;
  this.iesel.moveEnd("character", -secondtag.length);
   this.iesel.moveStart("character", -l);   
} else {
  this.iesel.text=text;
}
   this.iesel.select();
 } else if(this.start>=0&&this.end>=this.start) {
    var left=this.target.value.substring(0,this.start);
    var right=this.target.value.substr(this.end);
 var scont=this.target.value.substring(this.start, this.end);
 if(typeof(secondtag)=="string") {
   this.target.value=left+text+scont+secondtag+right;
   this.end=this.target.selectionEnd=this.start+text.length+scont.length;
   this.start=this.target.selectionStart=this.start+text.length;    
 } else {
      this.target.value=left+text+right;
   this.end=this.target.selectionEnd=this.start+text.length;
   this.start=this.target.selectionStart=this.start+text.length;
 }
 this.target.scrollTop=this.scroll;
 this.target.focus();
 } else {
   this.target.value+=text + ((typeof(secondtag)=="string")? secondtag: "");
if(this.scroll>=0) this.target.scrollTop=this.scroll;
 }
}
/**
* Это функции для веса =)
*/
TextAreaSelectionHelper.prototype.getText=function() {
 return this.target.value;
}
TextAreaSelectionHelper.prototype.setText=function(text) {
 this.target.value=text;
}
/**
* Приватная фукнкция, записывающая позицию курсора
*/
function _textareaSaver() {
 if(document.selection) {
   this.carretHandler.iesel = document.selection.createRange().duplicate();
 } else if(typeof(this.selectionStart)!="undefined") {
   this.carretHandler.start=this.selectionStart;
this.carretHandler.end=this.selectionEnd;
this.carretHandler.scroll=this.scrollTop;
 } else {this.carretHandler.start=this.carretHandler.end=-1;}
}

//Пример использования
function test() {
var text=new TextAreaSelectionHelper(document.getElementById("textarea1"));
text.setSelectedText("[test]","[/test]");
//text.setSelectedText("[test]");
}

Автор: Aliance 15.8.2005, 19:28
Код

<script src="insert.js"></script>
...
<textarea id="textarea1" cols=80 rows=20 name="AAAA">hhj</textarea>
<script type="text/javascript">var post=new TextAreaSelectionHelper(document.getElementById("AAAA"));</script>

Автор: -NIK- 4.11.2005, 12:28
эм, я очень слаб в JavaScript, это только для IE? Если да, то имхо есть вариант легче.

Код

<script>
function zzz()  {
  obj_ta=document.form.textarea;
  obj_ta.focus();
  var sel=document.selection;
  var rng=sel.createRange();
  rng.colapse;
  var podst="<b>"+rng.text+"</b>";
  rng.text=podst;
 }
</script>
<form name=form>
<input type=button value=" B " onClick=zzz()><br><br>
<textarea name=textarea cols=50 rows=10></textarea>
</form>


А готового кода для Мазиллы нет? Мне очень нужно, самому разобраться не получается. Помогите пожалуйста!

Автор: sergejzr 4.11.2005, 13:13
-NIK-, Sardar "только для ИЕ" не пишет smile

Автор: Aliance 4.11.2005, 13:21

Автор: Alx 21.1.2006, 16:57
я вот чего не понял. мы в мозилле, когда хотим заменить выделение, мы делим на три части, потом заменяем среднюю часть, опять соединяем в одну часть и полностью заменяем на неё содержимое инпута?

Автор: sergejzr 21.1.2006, 17:06
Цитата(Alx @ 21.1.2006, 14:57 Найти цитируемый пост)

я вот чего не понял. мы в мозилле, когда хотим заменить выделение, мы делим на три части, потом заменяем среднюю часть, опять соединяем в одну часть и полностью заменяем на неё содержимое инпута?

Именно так. В версии 1.5 возможно кое что и изменилось. Например document.selection теперь обьектом стал. Но это надо ещё посотреть что, да как. Пока некогда..

Автор: Alx 21.1.2006, 17:56
ок, спасибо.
решений для других браузеров, кроме IE и Firefox так и нету, да?

Автор: sergejzr 21.1.2006, 18:22
решение Сардара работает н всём, кроме оперы <8.0 (для старых опер решения нет. программисты там - пофигисты smile)

Автор: 3,14 11.4.2006, 17:46
У меня не работает в IE (пишет object doesn't support this property or method), если выделение текста в textarea сделать двойным кликом.
IE Version: 6.0.2900.2180.xpsp_sp2_gdr.050301-1519

Автор: SANCHOS 19.5.2006, 18:51
А чем вам не нравится более простой вариант для этого действия?

Код

<html>
<head>
<style>
a {text-decoration: underline; color: #0000FF; font-size: 14pt;}
</style>
</head>
<body>
<form name="form">
<INPUT TYPE="text" NAME="tb">
</form>
<a onclick="document.form.tb.value += 'text1' ">text1</a>
<a onclick="document.form.tb.value += 'text2' ">text2</a>
<a onclick="document.form.tb.value += 'text3' ">text3</a>
</body>
</html>


text1,tex2,text3 можно заменять чем угодно. 

Автор: godsgame 3.9.2006, 17:32
ммм.. потратил много времени пытаясь подключить эти ф-ции... Сначала получал ошибку Object not defined/expected или что-то в этом роде... потом решил для начала просто подключить файл..

Получил ошибку: Syntax Error on line 94

подключаю так:
Код

<script LANGUAGE="JavaScript" src='myjs.js'></SCRIPT>


файл:
Код

/**
* Обьект через который мы и будем работать с текстовыми полями.
* Содержит все необходимые поля, легко расширяем по вкусу =)
* @argument  obj  HTMLElment  - текстовое поле, с кторым предстоит работать
* 
* Author: Sardar <[email protected]>
*/
function TextAreaSelectionHelper(obj) {
  this.target=obj;
  this.target.carretHandler=this; //ссылка самого на себя для текстового поля
  /**
  * Помним, что события могут быть уже опредлеенны, тогда нужно использовать
  * очереди. Конечно, реализация для ИЕ и Мозиллы в корне различаются, почитать
  * и достать готовый код можно здесь: http://forum.vingrad.ru/index.php?showtopic=32350
  */
  this.target.onchange=_textareaSaver;
  this.target.onclick=_textareaSaver;
  this.target.onkeyup=_textareaSaver;
  this.target.onfocus=_textareaSaver;
  if(!document.selection) this.target.onSelect=_textareaSaver; //для Мозиллы
  
  this.start=-1;
  this.end=-1;
  this.scroll=-1;
  this.iesel=null; //для ИЕ
}
/**
* Достать отселектированный текст
*/
TextAreaSelectionHelper.prototype.getSelectedText=function() {
   return this.iesel? this.iesel.text: (this.start>=0&&this.end>this.start)? this.target.value.substring(this.start,this.end): "";
}
/**
* Вставить код под курсором. Если текст не отселектирован(не фокуса) и 
* позиция не взята, то вставить в конец текстового поля.
*
* @argument text String - заменить селекцию на этот текст
* @argument secondtag String - если задан, то селекция не заменяется, а обрамляется этими тегами
*/
TextAreaSelectionHelper.prototype.setSelectedText=function(text, secondtag) {
  if(this.iesel) {
    if(typeof(secondtag)=="string") {
   var l=this.iesel.text.length;
      this.iesel.text=text+this.iesel.text+secondtag;
   this.iesel.moveEnd("character", -secondtag.length);
    this.iesel.moveStart("character", -l);   
    } else {
   this.iesel.text=text;
    }
    this.iesel.select();
  } else if(this.start>=0&&this.end>=this.start) {
     var left=this.target.value.substring(0,this.start);
     var right=this.target.value.substr(this.end);
  var scont=this.target.value.substring(this.start, this.end);
  if(typeof(secondtag)=="string") {
    this.target.value=left+text+scont+secondtag+right;
    this.end=this.target.selectionEnd=this.start+text.length+scont.length;
    this.start=this.target.selectionStart=this.start+text.length;    
  } else {
       this.target.value=left+text+right;
    this.end=this.target.selectionEnd=this.start+text.length;
    this.start=this.target.selectionStart=this.start+text.length;
  }
  this.target.scrollTop=this.scroll;
  this.target.focus();
  } else {
    this.target.value+=text + ((typeof(secondtag)=="string")? secondtag: "");
    if(this.scroll>=0) this.target.scrollTop=this.scroll;
  }
}
/**
* Это функции для веса =)
*/
TextAreaSelectionHelper.prototype.getText=function() {
  return this.target.value;
}
TextAreaSelectionHelper.prototype.setText=function(text) {
  this.target.value=text;
}
/**
* Приватная фукнкция, записывающая позицию курсора
*/
function _textareaSaver() {
  if(document.selection) {
    this.carretHandler.iesel = document.selection.createRange().duplicate();
  } else if(typeof(this.selectionStart)!="undefined") {
    this.carretHandler.start=this.selectionStart;
    this.carretHandler.end=this.selectionEnd;
    this.carretHandler.scroll=this.scrollTop;
  } else {this.carretHandler.start=this.carretHandler.end=-1;}
}

..........
//Пример использования
//эта строка отработает после загрузки страницы.
//onload=function(){text=new TextAreaSelectionHelper(document.getElementById("myText"));}

function test() {
 text.setSelectedText("[test]","[/test]");
// text.setSelectedText("[test]");
}


Я даже закоментил ОНЛОАД... но и это не помогло.
Помогите пожалуйста разобраться... ЧЯто-то мне подсказывает что у меня руки кривые.

Автор: godsgame 3.9.2006, 17:51
нашел ответ тут:
Код

http://forum.vingrad.ru/index.php?showtopic=55935&st=15


спасибо Ciber SLasH !

Автор: GZep 11.2.2007, 21:55
Sardar, ваш вариант немного не верен...

Вот кусок вашего кода:
Код

  if(document.selection) {//84 строка

Тогда как вместо него надо:
Код

    if (document.selection && document.selection.createRange) {//IE 4.0 (win32) & Opera >= 8.0

Все дело в том, что mac версия ИЕ поддерживает document.selection, но не поддерживает document.selection.createRange smile .
Вот так вот. smile 

Теперь попрошу кого-нибудь помочь мне со своим кодом: (пример Sardar'a мне не подходит)
Собственно, что не работает:
Все прекрасно работает кроме изменения позиции курсора в ИЕ(у меня стоит 7-я финальная версия)...
С остальными браузерами все просто отлично. smile 

Вот код(прямо копируйте и вставляйте в файл для запуска, форма, кнопки и т.д. уже имеются):
Код

<script language="JavaScript" type="text/javascript">
<!--
function InsertTag(id, open, close) {
    var target = document.getElementById(id);
    if (!window.opera)//Даем фокус, если браузер не Opera
        target.focus();
    if (typeof(cursor) != "undefined") {//IE 4.0 (win32) & Opera 8.0
        //alert("IE & Opera");
        //Определяем, есть ли выделение(нам это надо сделать еще до кода встаки тега, иначе будет всегда true
        var haveSel = cursor.text.length ? true : false;
        //Если имеется выделенный участок строки - ставим курсор в конец
        //Иначе - между тегами
        
        //Собственное обводим выделенное тегами
        cursor.text = open + cursor.text + close;
        
        //Работаем с позицией курсора:
        
        if (haveSel) {//Ставим курсор в конец
            cursor.collapse(true);               
        } else {//Ставим курсор между тегами
            //Это тут я нахимичить пытался, вроде все правильно делал, но все-равно ничего не вышло
            //cursor.moveEnd("character", -close.length);
            //cursor.moveStart("character", -target.value.length);
            //cursor.move("character", -4);//target.value.length);            
        }
        cursor.select();//Честно говоря не совсем понимаю зачем эта строчка :)
    }
function PositionSaver(target) {
    if (document.selection && document.selection.createRange) {//IE 4.0 (win32) & Opera 8.0
        cursor = document.selection.createRange().duplicate();
        //cursor = target.createTextRange().duplicate();
        return;
    }
}
// -->
</script>
<form>
    <textarea
        onselect='PositionSaver(this);'
        onclick='PositionSaver(this);'
        onkeyup='PositionSaver(this);'
        onfocus='PositionSaver(this);'
        id="area"
        cols="100"
        rows="20">
    </textarea>
</form>
<a href="javascript:InsertTag('area', '', '');">[b]</a><br />
<a href="javascript:InsertTag('area', '[i]', '[/i]');">[i]</a>

Помогите пожалуйста! Не знаю, почему этот ие не хочет никак курсор двигать, ни в какую...  smile 

Автор: Sardar 12.2.2007, 00:28
Цитата(GZep @  11.2.2007,  20:55 Найти цитируемый пост)
Sardar, ваш вариант немного не верен...

Да там многое что нужно переделывать, добавлять поддержку множественных селекций и прочее. Руки не дойдут, пока у себя где не запользую, не сдвигаеться с места.

Коллапсировать селекцию это прировнять начало к концу или конец к началу, смотря какое направление. Следовательно если передвинуть конец и сколлапсировать селекцию, то передвинем курсор куда нужно. В твоём случае двигаем только если текста в селекции не было, следовательно достаточно только cursor.moveEnd("character", -close.length);.

Цитата(GZep @  11.2.2007,  20:55 Найти цитируемый пост)
cursor.select();//Честно говоря не совсем понимаю зачем эта строчка smile

На одном поле может быть масса селекций, это такие хендлеры по тексту, помогающие изменять его в некотором месте. Селекция на экране это нечто другое, она обычно одна. Вызовом .select() мы переносим селекцию на экране под конкретную селекцию в скрипте. Короче что бы курсор реально передвинулся smile

Автор: GZep 12.2.2007, 16:25
Цитата(Sardar @  12.2.2007,  00:28 Найти цитируемый пост)
только cursor.moveEnd

А cursor.moveStart в моем случае можно не вставлять? Это потому что отсутствует выделение, а имеется только позиция курсора?

А как тогда поставить курсор в ие в самый конец строки? Не выделенной части а всей строки целиком... smile 

Цитата(Sardar @  12.2.2007,  00:28 Найти цитируемый пост)
Вызовом .select() мы переносим селекцию на экране под конкретную селекцию в скрипте

Эта такая особенность в ие?

А вот еще проблема появилась:
Код

//кусок кода
        alert(cursor.text.length);//выводит количество выделенных символов
        cursor.text = open + cursor.text + close;
        alert(cursor.text.length);//всегда почему-то выводит "0"

Почему так?

Автор: Sardar 12.2.2007, 17:54
Цитата(GZep @  12.2.2007,  15:25 Найти цитируемый пост)
А cursor.moveStart в моем случае можно не вставлять? Это потому что отсутствует выделение, а имеется только позиция курсора?

Если не нужно ставить курсор, то не нужно, но тогда и .select() убери. "Курсор" == селекция с нулевой длинной.

Цитата(GZep @  12.2.2007,  15:25 Найти цитируемый пост)
А как тогда поставить курсор в ие в самый конец строки? Не выделенной части а всей строки целиком... 

Читай абзац выше. Отселектировать всё что нужно, сколлапсировать в начало или в конец. Можно и move(End|Start) использовать. Только не забудь .select() на конце, что бы реальная селекция на экране изменилась.

Цитата(GZep @  12.2.2007,  15:25 Найти цитируемый пост)
Эта такая особенность в ие?

Нет, это http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/obj_selection.asp?frame=true.

Цитата(GZep @  12.2.2007,  15:25 Найти цитируемый пост)
Почему так?

Потому что после установки текста селекция всегда коллапсируется. Сделай с неё дубликат, он по моему не должен изменяться. Хотя у ИЕ с дубликатами глюки есть, можно двигать концы одной селекции, а дубликат "магическим способом" тоже будет изменяться. Больше на эту тему в основном разделе о indent'aх в питоне, фича к форумскому полю ответа по ctrl+[].

Автор: GZep 12.2.2007, 20:11
Цитата(Sardar @  12.2.2007,  17:54 Найти цитируемый пост)
Можно и move(End|Start)

Вот я как раз это и применял, только толку без  .select() не было. Вот в этом, наверное, и была вся проблема.

Цитата(Sardar @  12.2.2007,  17:54 Найти цитируемый пост)
спецификация selection.

Вот я там был smile Только я там думал нормальных примеров найти и уже их использовать...

Цитата(Sardar @  12.2.2007,  17:54 Найти цитируемый пост)
Потому что после установки текста селекция всегда коллапсируется. Сделай с неё дубликат, он по моему не должен изменяться. Хотя у ИЕ с дубликатами глюки есть, можно двигать концы одной селекции, а дубликат "магическим способом" тоже будет изменяться. Больше на эту тему в основном разделе о indent'aх в питоне, фича к форумскому полю ответа по ctrl+[]

Спасибо, буду копать, думаю, скоро разберусь и выложу тут как сделать.

Sardar, Даже не знаю, что сказать. Такая помощь! Да чтобы я без Вас делал! smile 

Да, наверное позже выложу свой универсальный вариант, который, думаю, многим понравиться.

Автор: GZep 13.2.2007, 15:11
Цитата(Sardar @  12.2.2007,  17:54 Найти цитируемый пост)
Можно и move(End|Start) использовать

Sardar, помогите, пожалуйста, не работает.
Вот пример использования:
[только_что_вставленный_тег][текст][/только_что_вставленный_тег][курсор] - все правильно, мне так и надо!
[только_что_вставленный_тег][текст][/только_что_вставленный_тег][курсор][предыдущий_тег][текст][/предыдущий_тег] - нет, мне надо, чтобы курсор в самом конце стоял!

По-моему надо расширить конечную точку селекции до конца формы ввода, чтобы добиться нужного результата... Только вот не получается...

Вот что нахимичил...:
Код

        if (haveSel) {//"Сварачиваем" выделение и ставим курсор в конец
            //cursor.collapse(true);
            //alert("haveSel");
            //cursor.expand("textedit");
            cursor.setEndPoint("EndToEnd", cursor);
            //alert(cursor.compareEndPoints("EndToEnd", target));
            //cursor.move("character", end);
            cursor.move("textedit", end);
        //move(End|Start)
        //move("character",0)
        }
        cursor.select();

Это только кусок, естественно, кода.

Автор: Sardar 14.2.2007, 17:21
Не знаю зачем ставить курсор всегда в конец поля, но можно просто отселектировать всё содержимое и коллапсировать селекцию в конец. 
Обернуть поле в селекцию можно методом http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/movetoelementtext.asp.

Автор: GZep 17.2.2007, 21:34
Вот так, вроде, должно работать, но опять smile 
Код

    if (typeof(cursor) != "undefined") {//IE 4.0 (win32) & Opera 8.0
            cursor.text = open + cursor.text + close;//Обводим выделенное тегами
            //Работаем с позицией курсора:
            //cursor.collapse(true);
            //cursor.expand("textedit");
            //cursor.setEndPoint("EndToEnd", cursor);
            //alert(cursor.compareEndPoints("EndToEnd", target));
            cursor.moveToElementText(target);
            //cursor.moveEnd("character", -close.length);
            cursor.move("character", end);//"Сварачиваем" выделение и ставим курсор в конец
            //cursor.move("textedit", end);
            //move(End|Start)//move("character",0)
        cursor.select();//Переносим селекцию
        return;//Завершаем работу функции.
    }

Просто на самом деле уже интересно: получится так сделать или нет. smile 
Sardar, посмотрите, пожалуйста, что там не так?

Автор: Sardar 17.2.2007, 22:28
Цитата(GZep @  17.2.2007,  20:34 Найти цитируемый пост)
cursor.move("character", end);//"Сварачиваем" выделение и ставим курсор в конец

Это вообще откуда? Как end вычислил? И вообще зачем, если ты уже отселектировал всё. Я сказал коллапсировать, т.е. .collapse(true)

Автор: GZep 18.2.2007, 12:21
Sardar, спасибо, получилось smile 
Описание кода:
  • Если имеется выделенный участок, то он обводится тегами, а курсор ставиться в самый конец, для дальнейшего набора текста.
  • Если браузер не поддерживает выделение, то в конец добавляется один "открывающий" тег или парный ему "закрывающий", если уже есть "открывающий" тег.
  • Прадусматривается mac версия ИЕ, где не поддерживается объект document.selection.createRange
  • Предусматривается отмена передачи фокуса если браузер - Opera
  • Поддержка нескольких форм ввода
Вот код. Форма воода имеется, можно прямо сохранять и тестить. О багах пишите в эту тему.
Код

<script language="JavaScript" type="text/javascript">
<!--
InsertTag_tags = new Array();//Массив, необходимый для стрых браузеров, которые не поддерживают выделение
function InsertTag(id, open, close) {
    var target = document.getElementById(id);
    if (!window.opera)//Даем фокус, если браузер не Opera
        target.focus();
    if (typeof(cursor) != "undefined") {//IE 4.0 (win32) & Opera 8.0
        //alert("IE & Opera");
        if (cursor.text.length) {//Если имеется выделение
            cursor.text = open + cursor.text + close;//Обводим выделенное тегами
            cursor.moveToElementText(target);//Расширяем облость селекции на всё поле ввода
            cursor.collapse(false);//"Сварачиваем" выделение в курсор и ставим его в конец
        } else {//Если имеется курсор без выделенного участка строки
            cursor.text = open + close;//Вставляем теги на место курсора
            cursor.moveEnd("character", -close.length);//Ставим курсор между тегами
        }
        cursor.select();//Переносим селекцию
        return;//Завершаем работу функции.
    }
    if (typeof(cursorStart) != "undefined") {//Gecko
        var start = target.value.substring(0, cursorStart),//Получаем строку, находящуюся до выделения
            selected = target.value.substring(cursorStart, cursorEnd),//Получаем выделенную строку (или пустую строку, если ничего не выделено)
            end = target.value.substr(cursorEnd);//Получаем строку, находящуюся после выделения
        target.value = start + open + selected + close + end;//Изменяем значение объекта
        target.selectionStart = target.selectionEnd = selected.length ? target.value.length : start.length + open.length;//Устанавливаем позицию курсора
        target.scrollTop = cursorScroll;//Задаем scroll
        return;//Завершаем работу функции.
    }
    if (InsertTag_tags[open + close]) {//Если в глобальном массиве есть ключ с именем тека - добавляем вторую часть тега
        target.value += close;
        InsertTag_tags[open + close] = false;
    } else {//Если в глобальном массиве нету ключа с именем тега или этот ключ имеет значение false - добавляем в конец значения объекта первую часть тега
        target.value += open;
        InsertTag_tags[open + close] = true;
    }
}
function PositionSaver(target) {
    if (document.selection && document.selection.createRange) {//IE 4.0 (win32) & Opera 8.0
        cursor = document.selection.createRange().duplicate();
        return;
    }
    if (typeof(target.selectionStart) != "undefined") {//FireFox & Mozilla
        cursorStart = target.selectionStart;
        cursorEnd = target.selectionEnd;
        cursorScroll = target.scrollTop;
        return;
    }
}
// -->
</script>
<form>
    <textarea
        onselect='PositionSaver(this);'
        onclick='PositionSaver(this);'
        onkeyup='PositionSaver(this);'
        onfocus='PositionSaver(this);'
        id="area"
        cols="100"
        rows="20">
</textarea></form><br />
<a href="javascript:InsertTag('area', '', '');">[b]</a><br />
<a href="javascript:InsertTag('area', '[i]', '[/i]');">[i]</a><br />

Sardar, посмотрите, пожалуйста, кроссбраузерный ли этот код?

Автор: Sardar 18.2.2007, 14:42
Вроде кроссбраузерный. Есть пара замечаний:
  • не храни инфу в глобальных переменных, сейчас ты не сможешь сделать хендлер для более чем одного поля на странице
  • открытые теги лучше ложить на стек, а не по хеш-таблице проверять. Иначе порядок закрытия будет не верным

Автор: Rigel 24.7.2007, 11:05
Получается замечательно, но переход курсора в конец текста не всегда удобен - лучше бы он оставался приблизительно на месте редактирования - только вот никак не придумываетсяЮ как бы это сделать.  smile 

Автор: gridis 9.2.2008, 19:27
Sardar, здравствуйте, у меня такой к Вам вопрос....
Мне вашь пример очень понравился.... только у меня такая проблема.... мне нужна вставка HTML кода под курсором... в опере и файрфоксе  я нормально вставляю через EditField.execCommand('inserthtml',false, "");

ИЕ такой функции нет..... 

Автор: Sardar 10.2.2008, 02:30
Получили селекцию, меняем содержимое через http://msdn2.microsoft.com/en-us/library/ms536656.aspx(html)

Автор: introtel 1.6.2009, 14:28
спасибо за код. вопрос: а что делать если надо вставить текст под курсором. не ЗАМЕНИТЬ отселектированный текст, а как-то запомнить позицию курсора. в фф это можно а как быть с ИЕ?

Автор: GogElf 17.7.2009, 00:58
Сам я не местный  smile но хочу внести вклад.
Я не очень много понимаю в javascript (ну на стадии что-то сломать). Меня очень напрягает писать id и по ним чтоб код работал.  smile 
В общем кому хочется сделать 1 панельку с кнопками на 2-3 или более полей то заменяем:
Код

onload=function(){text=new TextAreaSelectionHelper(document.getElementById("textarea"));}

на
Код

function poz(names){
    text=new TextAreaSelectionHelper(names);
}

И вписываем onclick
Код

<textarea cols="80" rows="10" onclick="return poz(this)"></textarea>

 smile 

Автор: maxipub 31.3.2011, 21:48
Тема прикреплена... она еще жива?

Заметил странный баг в оригинальном скрипте. FF 3.6.13

Код

<script>
/**
* Обьект через который мы и будем работать с текстовыми полями.
* Содержит все необходимые поля, легко расширяем по вкусу =)
* @argument  obj  HTMLElment  - текстовое поле, с кторым предстоит работать
* 
* Author: Sardar <[email protected]>
*/
function TextAreaSelectionHelper(obj) {
  this.target=obj;
  this.target.carretHandler=this; //ссылка самого на себя для текстового поля
  /**
  * Помним, что события могут быть уже опредлеенны, тогда нужно использовать
  * очереди. Конечно, реализация для ИЕ и Мозиллы в корне различаются, почитать
  * и достать готовый код можно здесь: http://forum.vingrad.ru/index.php?showtopic=32350
  */
  this.target.onchange=_textareaSaver;
  this.target.onclick=_textareaSaver;
  this.target.onkeyup=_textareaSaver;
  this.target.onfocus=_textareaSaver;
  if(!document.selection) this.target.onSelect=_textareaSaver; //для Мозиллы
  
  this.start=-1;
  this.end=-1;
  this.scroll=-1;
  this.iesel=null; //для ИЕ
}
/**
* Достать отселектированный текст
*/
TextAreaSelectionHelper.prototype.getSelectedText=function() {
   return this.iesel? this.iesel.text: (this.start>=0&&this.end>this.start)? this.target.value.substring(this.start,this.end): "";
}
/**
* Вставить код под курсором. Если текст не отселектирован(не фокуса) и 
* позиция не взята, то вставить в конец текстового поля.
*
* @argument text String - заменить селекцию на этот текст
* @argument secondtag String - если задан, то селекция не заменяется, а обрамляется этими тегами
*/
TextAreaSelectionHelper.prototype.setSelectedText=function(text, secondtag) {
  if(this.iesel) {
    if(typeof(secondtag)=="string") {
   var l=this.iesel.text.length;
      this.iesel.text=text+this.iesel.text+secondtag;
   this.iesel.moveEnd("character", -secondtag.length);
    this.iesel.moveStart("character", -l);   
    } else {
   this.iesel.text=text;
    }
    this.iesel.select();
  } else if(this.start>=0&&this.end>=this.start) {
     var left=this.target.value.substring(0,this.start);
     var right=this.target.value.substr(this.end);
  var scont=this.target.value.substring(this.start, this.end);
  if(typeof(secondtag)=="string") {
    this.target.value=left+text+scont+secondtag+right;
    this.end=this.target.selectionEnd=this.start+text.length+scont.length;
    this.start=this.target.selectionStart=this.start+text.length;    
  } else {
       this.target.value=left+text+right;
    this.end=this.target.selectionEnd=this.start+text.length;
    this.start=this.target.selectionStart=this.start+text.length;
  }
  this.target.scrollTop=this.scroll;
  this.target.focus();
  } else {
    this.target.value+=text + ((typeof(secondtag)=="string")? secondtag: "");
    if(this.scroll>=0) this.target.scrollTop=this.scroll;
  }
}
/**
* Это функции для веса =)
*/
TextAreaSelectionHelper.prototype.getText=function() {
  return this.target.value;
}
TextAreaSelectionHelper.prototype.setText=function(text) {
  this.target.value=text;
}
/**
* Приватная фукнкция, записывающая позицию курсора
*/
function _textareaSaver() {
  if(document.selection) {
    this.carretHandler.iesel = document.selection.createRange().duplicate();
  } else if(typeof(this.selectionStart)!="undefined") {
    this.carretHandler.start=this.selectionStart;
    this.carretHandler.end=this.selectionEnd;
    this.carretHandler.scroll=this.scrollTop;
  } else {this.carretHandler.start=this.carretHandler.end=-1;}
}

//Пример использования
//эта строка отработает после загрузки страницы.
onload=function(){text=new TextAreaSelectionHelper(document.getElementById("textarea"));}
function test() {
 text.setSelectedText("[test]","[/test]");
// text.setSelectedText("[test]");
}
</script>

<textarea id="textarea" cols=80 rows=20>0 qwerty asdfgh zxcvbn
1 qwerty asdfgh zxcvbn
2 qwerty asdfgh zxcvbn
3 qwerty asdfgh zxcvbn
4 qwerty asdfgh zxcvbn
5 qwerty asdfgh zxcvbn
6 qwerty asdfgh zxcvbn
7 qwerty asdfgh zxcvbn
8 qwerty asdfgh zxcvbn
9 qwerty asdfgh zxcvbn</textarea>
<span onClick="test()">Test</span>


Оригинальный код, просто с прописанными данными для textarea.

Если выделить мышкой несколько строк, но так, чтоб одна из строк была выделена не полностью, например:

Код

4 qwerty asdfgh zxcvbn
5 qwerty asdfgh zxcvbn
6 qwerty asdfgh


После клика по "Test" будет выполнена некорректная ставка.

При выделении мышкой в FF у меня все корректно работает только если выделена лишь часть одной строки, либо несколько строк, но полностью.

В общем, очень жаль, наработка вроде ничего, но работает почему-то некорректно. Судя по всему, что-то с обработкой событий.

ЗЫ: вот и в IE 8 тоже нашел небольшой баг, но это еще терпимо. К примеру, выделяем строки:

Код

3 qwerty asdfgh zxcvbn
4 qwerty asdfgh zxcvbn
5 qwerty asdfgh zxcvbn
6 qwerty asdfgh zxcvbn
7 qwerty asdfgh zxcvbn


После клика по "Test" будет выполнена корректная ставка, но с некорректным выделением (что выделяется - указано жирным):

[test]3 qwerty asdfgh zxcvbn
4 qwerty asdfgh zxcvbn
5 qwerty asdfgh zxcvbn
6 qwerty asdfgh zxcvbn
7 qwerty asdfgh zxcvbn
[/test]

Т.е. он начало выделения смещает на минус (количество переводов в выделенной строке - 1).

Как убрать эти баги? Особенно первый. Или подскажите альтернативу. Сам писал-писал, не вышло. Другие варианты смотрел - не подходят.

Необходимо чтоб после вставки текста под курсор, texarea всегда подскролливался к курсору (к месту вставки).

Только чтоб без лишних библиотек и наворотов. Обычная же вещь, с чего сложности?

Заранее благодарю!

Автор: maxipub 31.3.2011, 23:14
Если не понятно о чем речь, могу снять видео и выложить чтоб быстрее было.

Кстати, тут на форуме прогеров (хе-хе smile) данная функция тоже не совсем чисто работает. Но то что тут - это уже мелочи. Вообще как на vBulletin сделано - идеал. По крайней мере, так и не нашел к чему докопаться. Другое дело, что что тут, что там - js-ы все шифрованные, собаки.

Автор: TsibaSasha 6.3.2012, 03:05
Браво smile 
Уважаю, столько сил и терпения надо иметь, что бы написать такой код для форума)

Автор: Sanchezzz 17.9.2012, 12:52
Предлагаю свое решение с использованием плагина caret
Пощупать можно http://jsfiddle.net/VduMQ/1/


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)