HTML съдържание с възможност за редактиране с нередактируеми острови

Имам нещо като WYSIWYG редактор, базиран на браузър, където потребителите могат да редактират документи-шаблони.

Document-template е обикновен html с някои специални "заместители на код за сливане". Такъв шаблон се „инстанцира“ чрез заместване на тези контейнери с данни, идващи от DB. Това дава окончателен документ - екземпляр на шаблона.

Сегашният ми подход изглежда така:

<div contenteditable>
  Sample template with <input type=button class="mergecode" value="MergeCode1">.
</div>

(Онлайн пример за игра: http://jsfiddle.net/tFBKN/ )

Входът не може да се редактира в такъв случай и се държи като плътен блок - точно това, от което се нуждая. Потребителите могат да изтрият такива кодове за сливане, като щракнат върху DEL или BACKSPACE като всички други знаци и т.н. Като имам подходящ CSS за такъв input.mergecode, мога да постигна желаното от мен усещане.

Но с такъв подход имам три различни проблема в три различни UA:

  • IE - CSS { font:inherit } просто не работи там, така че ако входът е вътре в <b> като тук <b><input value="test"></b>, той не наследява никакви стилове на шрифтове.
  • FF - Копие на фрагмент, който съдържа елемент <input>, премахва този вход от съдържанието на клипборда, така че по-нататъшната операция за поставяне вмъква всичко, но не и входове.
  • GC – Кликнете върху {BACKSPACE} веднага след като <input> даде странни резултати (бъг)

Така че търся други идеи как да представя нередактируеми вградени блокове, подобни на "острови" в HTML.

Друг подход, който съм опитвал досега:

  • <span contenteditable="false">MergeCode1</span> - не работи, тъй като повечето UA премахват такъв възел от селекцията. Така че не е възможно, да речем, да приложите <b> или <i> върху селекция, която съдържа такъв интервал.

Някакви други идеи?


person c-smile    schedule 30.01.2013    source източник
comment
Невъзможно. contenteditable засяга всички дъщерни елементи. Можете да използвате JS за замяна, ако бъде изтрит.   -  person Mooseman    schedule 31.01.2013
comment
@Mooseman Съжалявам, какво точно не е възможно?   -  person c-smile    schedule 31.01.2013
comment
Коментарът на @Mooseman е грешен; contenteditable може да бъде заменен за дъщерни елементи, както в примера <span contenteditable="false">MergeCode1</span>. Както се обяснява във въпроса, този естествен подход обаче не работи добре със селекции (напр. трикратното щракване не избира цял блок).   -  person Jukka K. Korpela    schedule 31.01.2013
comment
Опитайте този код: <div style="background:#000; color:#FFF" contenteditable=true> Hello, world! <span contenteditable="false" style="font-weight:bold">Unedtiable</span> Hello, world! </div> Както можете да видите, обхватът все още може да бъде премахнат. Текстът „Не може да се редактира“ не може да се редактира, но <span> може да бъде премахнат цяло от потребителя. Инструментите за разработка на Chrome показват, че целият блок е премахнат от DOM.   -  person Mooseman    schedule 31.01.2013
comment
@Mooseman, както казах, вече опитах contenteditable=false. Не работи в случай, че трябва да приложите участък като <b> върху такъв остров. Опитайте това: jsfiddle.net/RYsvZ/1, изберете текст с MergeCode1 вътре и щракнете върху CTRL- аз например. Ще видите текст в курсив, но не и MergeCode1. Използвайте DOM инспектора, за да видите проблема.   -  person c-smile    schedule 31.01.2013
comment
Интересно как се прилагат <b>s. Не намерих никаква спецификация, свързана с поведението на дъщерните елементи, освен че по подразбиране е inherit. Поведението, което описахте, което виждам в Chrome, където <span> не е в курсив или удебелено, ми се струва правилно. contenteditable се прилага за промяна на текст, но също и за форматиране. JS може да помогне тук, но предполагам, че не искате да отидете там сега.   -  person Mooseman    schedule 31.01.2013
comment
@Mooseman: Не искам да променя съдържанието на елемента contenteditable=false, просто искам да обвия селекцията, която го съдържа, в <b> span. Chrome предотвратява това по неизвестни причини.   -  person c-smile    schedule 31.01.2013
comment
Начинът, по който го виждам, но без удебеляване на интервала, Chrome зачита възможността за редактиране на съдържанието.   -  person Mooseman    schedule 31.01.2013
comment
@c-smile В крайна сметка останахте ли с метода ‹input type=button›? Или сте тръгнали по пътя на ръчно обработване на всички събития? Компромисът тук изглежда жалък :(   -  person sthomps    schedule 20.10.2013


Отговори (6)


Аз съм разработчик на CKEditor. Имаме известен опит с вложени елементи само за четене (т.е. плъгин placeholder и функцията, върху която работим в момента #9764) и нямам добри новини. Трябва да управлявате всяко поведение ръчно, ако искате да имате последователен вид и усещане. Няма трикове, които да оправят браузърите. И много неща (като това със странни неща, случващи се около въвеждане на GC) изглеждат неразрешими.

person Reinmar    schedule 04.02.2013
comment
Благодаря за отговора, изглежда, че не е само мой проблем. Този факт прави тази кора малко по-малко горчива, но все пак. Все пак благодаря за отговора. - person c-smile; 05.02.2013
comment
@Reinmar Закъсняхте за партито, но... измислихте ли някакво решение за този проблем? наздраве! - person pelican_george; 20.06.2016

Още една идея, която изглежда обещаваща:

За да използвате празен интервал с ::before { content:"caption"; }, който трябва да създаде блок без възможност за редактиране, представен в DOM като възел без позиции на каретка вътре.

Можете да опитате тук http://jsfiddle.net/TwVzt/1/

Но този подход не е лишен от проблеми (в моя случай): Няма начин да декларирате ::before inline с помощта на style DOM атрибут и така целият набор трябва да бъде деклариран в CSS предварително. Но наборът от кодове за сливане, който имам, е доста голям, дори неизвестен предварително изцяло в някои случаи на употреба. Въздишка.

Въпреки това поставям тази рецепта тук, ако някой има по-добри обстоятелства (известен набор от кодове).

person c-smile    schedule 02.02.2013
comment
Работи като чар! Използва се data-attributes за динамично съдържание вътре в ::before. - person Artyom Neustroev; 30.04.2014
comment
Да, това е хубаво решение. Също така намерих тази техника тук: HTML5 data attributes: hacks.mozilla.org/2012/10/ и видеоклип на тази страница: youtube.com/watch?v=On_WyUB1gOk (6 минути) - person Roelof Berkepeis; 02.08.2014
comment
МНОГО ВИ БЛАГОДАРЯ! Този подход, комбиниран с динамичен текст чрез атрибут на данни, изчисти основна пречка за мен. - person Floscher; 01.08.2016
comment
За да добавите допълнителна светлина към тази публикация и коментари: <span data-something="awesome"></span> и span:before{content:attr(data-something);} И е достатъчно лесно да създадете <style>...</style> блок с псевдо съдържание и стилове, генерирани динамично с JS, и след това да го добавите към главата. - person bit-less; 01.11.2017

Знам, че е стара тема, на която попаднах с подобен проблем. Просто имах нужда от няколко нередактируеми участъка в редактируем div. В крайна сметка внедрих HACK-AROUND като...

$('#testDiv').on('keydown', function(e) { 
    var targetNode = getSelectionStart();
    if(targetNode != undefined && targetNode.nodeType === 1 && targetNode.nodeName == 'SPAN')    
    {
      var nodeHtmlString = targetNode.outerHTML;

      if(~nodeHtmlString.indexOf("nonEditable"))
      {
        e.preventDefault();
        e.stopPropagation();
      }
    }
});

function getSelectionStart() {
 var node = document.getSelection().anchorNode;
 return (node.nodeType == 3 ? node.parentNode : node);
}

Завършете цигулката на https://jsfiddle.net/fxmb3nL6/20/

person Pravin    schedule 29.12.2015

Решение по-долу - с малко предупреждение. Но първо -- вие сте страхотни!
Не съм JS или HTML експерт, но също така не знаех за jsfiddle.net. И така, четейки вашите коментари плюс други онлайн търсения и някои тестове на jsfiddle, създадох следния код, който работи.

<pre><code>
<div contenteditable="false" class="editor" readonly="true" UNSELECTABLE="ON">
    Sample template with <span class="mergecode test1" />.
    <span contenteditable> editable </span>
    <font color=red><span UNSELECTABLE="ON" contenteditable="false" readonly="true"
     UNSELECTABLE="ON">  uneditable1 </span></font>
    <span contenteditable> editable2 </span>
    <font color=red><span contenteditable=false readonly="true" UNSELECTABLE="ON"> uneditable3</span></font>  
    <span contenteditable> editable4 </span>
    <span contenteditable="false" readonly="true" UNSELECTABLE="ON"> uneditable5 </span>
< /div>
</code></pre>

Ако поставите това в jsfiddle на chrome, ще видите, че работи в chrome (и IE и FF, но малко по-различно), но изглежда има малко предупреждение.

Уговорката е какво прави вашето свързване на клавиши с Backspace! В моя случай, когато избера фраза, която не може да се редактира, и натисна backspace в chrome, изглежда, че се опреснява или правя „отиване до последната страница“! Направих първите две нередактируеми червени (последното оставих черно), за да можете да видите какво се случва. Тествам го на jsfiddle в Chrome и във FF и в IE. Изглежда, че всичко е действало правилно, независимо от проблема с връщането назад.

И мисля, че това е логиката на това как работи. Първият Div е родител от най-високо ниво и е contenteditable="false". Така че всичко под него трябва да не може да се редактира. ОСВЕН децата в него, които имат span contenteditble="true", те стават редактируеми. Така че наследявате от вашия родител, но след това можете да презапишете като дете.

Сложих и таговете readonly="true" UNSELECTABLE="ON", защото бях чел някъде за тях. И трябваха още тестове!!! ЗАБЕЛЕЖКА, МОЛЯ, трябваше да взема Font... извън обхвата, или нямаше да работи. Но в IE работи перфектно. Нередактираните елементи бяха в какъвто цвят им казах да бъдат и никакво количество единично, двукратно или тройно щракване нямаше да избере нередактираните елементи в jsfiddle!!! :-) За Chrome единичното щракване не направи нищо, двойното щракване избра нередактируемото, а с backspace стана малко луд! Изглеждаше, че ще отиде на предишната страница. С FF нещата бяха малко по-луди! Щракнете два пъти върху нередактируемото и редактируемото преди него! Нямам идея защо.

Ако някой може да разбере и да напише за проблемите с Chrome и FF, ще бъде страхотно. Би било хубаво да имате двойно кликване или произволен брой бързи кликвания, за да не работите и в Chrome и FF. Отново искам да подчертая обхвата... изглежда трябва да е до нередактираните и шрифта... извън обхвата.

Дано всичко това има смисъл.

person user96265    schedule 25.09.2014
comment
Използвах това, за да имам вградени променливи в текстов редактор, така че това работи напълно за мен, благодаря! - person electrikmilk; 27.06.2019

Ето решение, което използвам в проект:

<div>
  <span contenteditable>Editable text</span> <span> - Non editable text</span>
</div>

Със съответния CSS, за да видите резултата:

div {
  border: 1px solid black;
  display: inline-block;
  max-width: 200px;
}

Ето резултата: http://www.cssdesk.com/rULJM

person tomfl    schedule 27.10.2019
comment
Да, но вие решавате съвсем различна задача: редактируеми острови в нередактируем текст. - person c-smile; 28.10.2019

За IE опитайте това:

<span contenteditable="true">Uneditable</span>

Обърнете внимание на обратната стойност на атрибута contenteditable! Това противоречи на всеки здрав разум, но работи - поне в IE8 и IE9. Този елемент, който не може да се редактира, можете да местите и да копирате/поставяте. Това обаче е далеч от съвършенството. напр. ако удебелите регион, който обхваща нередактируемия елемент, това също ще зарази съдържанието на нередактируемия елемент. Така че вероятно ще трябва да използвате някои скриптове, за да запазите здравината на елемента. Също така настройката contenteditable="true" ще украси нередактируемия елемент с манипулатори за преоразмеряване. Можете да ги изключите отново, като зададете unselectable="on", но това ще предотврати влаченето на обекта. Отново вероятно ще трябва да разчитате на скрипт, който възстановява ширината и височината.

person meringue    schedule 31.01.2013
comment
За съжаление този подход не работи в GC и FF - ако изберете текста, който съдържа такъв <span> и извикате document.execCommand('bold'), за да приложите <b> към селекцията, обхватът ще бъде изключен от селекцията. Ще видите нещо подобно: <b>text before</b><span contenteditable=false>Test</span><b>text after</b>. - person c-smile; 31.01.2013