Выделение одиночных тэгов
Вложений: 1
Привет!
Разрабатываю html-редактор. Чтобы изменить свойства какого либо объекта, понятное дело, требуется его выделение. Для "двойных тэгов" (типо <tag></tag>) проблем никаких не возникает, так как ссылку на объект легко получить через parentNode относительно позиции курсора. А вот с выделением одиночных тэгов (таких как <br/>, <hr/>, <input/>, <img/> и прочие) столкнулся с проблемой. Никак не могу сообразить, как узнать что пользователь выделил одиночный тэг? Ниже привожу код, который отвечает за "сборку" дерева нодов относительно текущей позиции курсора. При каждом изменении позиции важно определить, что либо выделяется некая область редактируемого текста, либо просто происходит переменещие по нему. При вызове getTreeNodes() опередяется с какого нода будет собираться дерево нодов, вплоть до <body>. Соответсвенно нужно знать: - либо если выделена вся нода, сборка начнётся с неё - либо сборка начнётся с родительского нода; тут же, если выделен какой то участок, он заносится в clipboard (для каких либо дальнейших операций). Выделение одиночных тэгов в коде у меня определяются как range.commonAncestorContainer с длиной выделенного текста 0. То есть подобно тому, что курсор установлен где то в тексте без выделения. Кто знает, как получить ссылку на выделенный объект, помогите разобраться?:help: jsCore.storage={ parentObject: null, range: null, buffer: '', clipboard: null, savedCopy: null, tree: null, /** * Функция получения выделенного объекта * срабатывает при любом изменении позиции текстового курсора */ get: function(){ //объект источника данных obj=arguments[1]||richedit||null; //событие, вызвавшее функцию e=arguments[0]||window.event // jsCore.debug(e,17,'storage.js'); // e=e||window.event||null; // var target=(e!=null && e.target!=null ? e.target : e.srcElement); var range=null; //Gecko, Opera if(jsCore.browser.isGecko() || jsCore.browser.isOpera()){ //Если выделение произвелось в редактируемой области if(obj.contentWindow.getSelection){ //Выделенная область var buffer=obj.contentWindow.getSelection(); //Сохраняем объект range range=buffer.getRangeAt(0); //Если ничего не выделено if(range.offsetStart==range.offsetEnd && range.startContainer==range.endContainer && buffer.toString().length==0){ //Для дальнейшей работы потребуется ссылка на выделенный объёкт jsCore.storage.range=range; //Если выделена какая то область }else{ //Создаем объект, с помощью которого можно получить HTML содержимое выделенного объекта jsCore.storage.clipboard=jsCore.dom.create.tag('span'); //В clipboard сохраняем HTML-содержимое выделенного объекта jsCore.storage.clipboard.appendChild(range.cloneContents()); //В buffer сохраняем текст выделенного объекта jsCore.storage.buffer=buffer; jsCore.storage.range=range; } return true; } //IE }else if(jsCore.browser.isIE()){ //Если выделение произвелось в редактируемой области if(re_doc.selection){ range=re_doc.selection.createRange(); jsCore.storage.buffer=range.text; jsCore.storage.clipboard=range.htmlText; return true; }else{ jsCore.storage.clear(); return false; } }else{ jsCore.storage.clear(); return false; } return true; }, getTreeNodes: function(){ jsCore.storage.get(richedit); if(jsCore.storage.range){ var o=null; jsCore.debug(jsCore.storage.range,67,'storage.js::range'); if(jsCore.storage.clipboard && jsCore.storage.clipboard.childNodes && jsCore.storage.clipboard.childNodes.length==1 && jsCore.storage.clipboard.childNodes[0].nodeType!=3 && jsCore.storage.clipboard.childNodes[0].innerHTML==jsCore.storage.buffer){ o=jsCore.storage.range.startContainer.childNodes[0]; jsCore.debug(o,74,'storage.js::currentNode'); }else{ if(jsCore.storage.range.commonAncestorContainer.nodeType!=3){ o=jsCore.storage.range.commonAncestorContainer; jsCore.debug(o,78,'storage.js::commonAncestorContainer'); }else{ o=jsCore.storage.range.commonAncestorContainer.parentNode; jsCore.debug(o,81,'storage.js::parentNode'); } } if(o){ if(o.tagName==null){ jsCore.debug(o,86,'storage.js::-=[ !!! BUG !!! ]=-'); }else{ if(o.tagName.toLowerCase()!='html'){ jsCore.storage.tree=new Array(); while(o && o.tagName.toLowerCase()!='body'){ if(o.tagName){ jsCore.storage.tree.push(o); o=o.parentNode; }else{ o=o.parentNode; } if(o==null) break; if(o.tagName==null) o=o.parentNode; } if(o) jsCore.storage.tree.push(o); } } } } } }; |
Прямо море ответов... Ну да ладно, ответ сам нашёл и как обычно, ларчик просто открывался. На самом деле, недоработка заключалась в том, что перед тем как получать текущую позицию курсора, нужно проверить, возможно пользователь выделил какой то объект. А отловить это можно перед определением range через getSelection().anchorNode + getSelection().anchorOffset:
var buffer=obj.contentWindow.getSelection(); //Если выделен какой то объект... if(buffer.anchorNode.childNodes[buffer.anchorOffset]){ //...сохраняем его в хранилище jsCore.storage.parentObject=buffer.anchorNode.childNodes[buffer.anchorOffset]; } //Сохраняем объект range range=buffer.getRangeAt(0); /*** ну и далее по коду с минимальными изменениями ***/ Собственно в хранилище объект попадёт только тогда, когда курсор "попал" непосредственно на объект. Методом перемещения курсора можно получить <br/>, а <input>, <img> - методом клика по ним. |
В продолжении истории, добрался теперь до злосчастного IE.
<html> <head> <title>Test page</title> <meta http-equiv="Content-Type" content="text/html; charset=windows-1251"> <script language="javascript"> if(window.captureEvents){ window.captureEvents(Event.CLICK); window.onclick=handle; }else document.onclick=handle; function handle(e){ var el=(typeof event!=='undefined')?event.srcElement:e.target; alert(el.tagName); } </script> </head> <body contenteditable="true"> <div>Any text into tag div</div> <br /> <img src="images/misc/float_above.gif" /> <hr /> <table width="100%" border="1"> <tr> <td>Cell 1</td> <td>Cell 2</td> <td>Cell 3</td> </tr> </table> </body> </html> Данный код в браузере IE (тестировал в 6 и 7 версиях) событие onclick на тэгах <IMG /> и <HR /> срабатывает только при повторном клике на элементах (именно повторный onclick, а не ondblclick). Как сделать так, чтобы оно срабатывало при первом клике? |
Мда... тихо сам с собою, я веду беседу =)
... if(window.captureEvents){ window.captureEvents(Event.CLICK|Event.MOUSEUP); window.onclick=window.onmouseup=handle; }else document.onclick=window.onmouseup=handle; ... |
если хочешь, можешь со мной поговорить ;)
|
Опыт разработки JavaScript WYSIWYG-редакторов далеко не у всех JavaScript-программистов есть :)
Вот кстати, может сталкивались с такой проблемой: <html> <head> <script> document.onmouseup = function() { alert(document.selection.createRange().htmlText); }; </script> </head> <body> <ul> <li><a href="…">link</a></li> </ul> </body> </html> Если поставить курсор перед ссылкой или выделить ссылку, всеравно в TextRange.htmlText будет outerHTML всей ссылки, как в этом случае определить, что выделение схлопнуто? |
Цитата:
Цитата:
Цитата:
<html> <head> <script language="javascript"> var init=function(){ document.onmouseup=function(){ var range=document.selection.createRange(); var buffer=range.text; var curEl=(range.length>0)?range.item(0):range.parentElement(); var curTag=curEl.tagName; var str=''; if(buffer) str+='Выделен текст: '+buffer+"\n"; if(curTag) str+='Текущий тэг: '+curTag+"\n"; if(range.length>0) str+='Одиночный тэг'; else str+='Содержимое тэга: '+curEl.innerHTML; alert(str); }; } </script> </head> <body contenteditable="true" onload="init()"> <img src="mypicture.png" /> <hr /> <ul> <li><a href="#">link</a></li> </ul> <table> <tr> <td>Cell 1</td> <td>Cell 2</td> <td>Cell 3</td> </tr> </table> </body> </html> Вся задача разрешается в этом маленьком участке кода (range.length>0) ? range.item(0) : range.parentElement(); Собственно, если выделен одиночный тэг, в range появится элемент, доступ к которому можно получить через range.item(0). Честно подсмотрел в чужих примерах, сам бы фик когда догадался до этого!!!! :blink: В ином случае мы вызываем функцию parentElement(), которая вернёт ссылку на объект родителя. |
Я спрашивал, как определить, схлопнуто выделение или нет, если курсор стоит перед ссылкой, т.е. мне нужно было реализовать аналог свойства Range.collapsed.
Оказывается, если курсор перед ссылкой, то в TextRange.htmlText будет HTML-код ссылки, но без текста: <a …></a> ну а у выделенной ссылки естественно все на месте: <a …>текст ссылки</a> Решение: 1)дублируем TextRange; 2)коллапсируем копию в начало; 3)если конец выделение совпал с началом копии (range1.compareEndPoints("EndToStart", range2) == 0), значит выделение схлопнуто (collapsed = true). |
Часовой пояс GMT +3, время: 16:11. |