私は良いエレガントな解決策を見つけました:
textselector.js (マークの選択 - ブックマーを CKEDITOR に挿入)
 function selectText(irtId, startIdSelectionId, endIdSelectionId) {
    var editor = CKEDITOR.instances[irtId];
    if (editor.getSelection().getRanges()[0].collapsed) {
        document.getElementById(startIdSelectionId).value = "";
        document.getElementById(endIdSelectionId).value = "";
    } else {
        var bookmarks = editor.getSelection().createBookmarks(true);
        var startId = bookmarks[0].startNode;
        var endId = bookmarks[0].endNode;
        document.getElementById(startIdSelectionId).value = startId;
        document.getElementById(endIdSelectionId).value = endId;
    }
    return true;
}
サーバー側でのアクション (競合を特定する - タグの交差、必要に応じて要素を修復する - 2 つの部分に分割してスパンを挿入する):
/**
 * prida span na oznacene html osestreni nezadoucich pripadu: prazdny
 * select, kolize s jinym spanem
 */
public void spanSelectedHtml() {
    // parsovani celeho dokumentu
    Document doc = Jsoup.parse(value);
    // osetreni chybovych stavu
    // - prazdny select
    if (startIdSelection.isEmpty() || endIdSelection.isEmpty()) {
        return;
    }
    // nalezeni znacek
    Element es = doc.getElementById(startIdSelection);
    Element ee = doc.getElementById(endIdSelection);
    // - konflikt s jinou znackou
    // bude doplneno
    // oprava okoli znacek v pripade nutnosti
    repairIfNecessary(es, ee);
    // vytvoreni span tagu s nalezitymi atributy
    Element span = doc.createElement("span");
    span.attr("property", clicked.getUrl());
    span.attr("class", clicked.getStyleClass());
    // nahrazeni prvni znacky span tagem
    es.replaceWith(span);
    // pripojeni vsech nasledujicich uzlu az do koncove znacky
    while (span.nextSibling() != ee) {
        span.appendChild(span.nextSibling());
    }
    // odstraneni koncove znacky
    ee.remove();
    // aktualizace hodnoty textove komponenty
    value = doc.toString();
}
/**
 * oprava okoli elementu v pripade nutnosti - pri zjisteni unikatniho rodice
 *
 * @param e1 prvni element
 * @param e2 druhy element
 */
private void repairIfNecessary(Element e1, Element e2) {
    while (hasUniqueParent(e1, e2)) { // unikatni rodice e1?
        repairElement(e1);
    }
    while (hasUniqueParent(e2, e1)) { // unikatni rodice e2?
        repairElement(e2);
    }
}
/**
 * oprava okoli elementu: rozdeleni na dve casti a vymazani rodice,
 * zachovani atributu
 *
 * @param e element s okolim na opravu
 */
private void repairElement(Element e) {
    // "problemovy rodic", ktereho je treba rozdelit
    Element p = e.parent();
    // 1. cast - pred znackou
    if (e.previousSibling() != null) { // osetreni null
        Element n = p.clone().empty(); // vkladany element musi byt formalne stejny
        p.prependChild(n); // umisteni elementu na zacatek - jako 1. dite
        while (n.nextSibling() != e) { // dokud se nedostanu ke znacce, pridavam uzly
            n.appendChild(n.nextSibling());
        }
    }
    // 2. cast - za znackou
    if (e.nextSibling() != null) { // osetreni null
        Element n = p.clone().empty(); // vkladany element musi byt formalne stejny
        p.appendChild(n); // umisteni elementu na konec - jako posledni dite
        while (n.previousSibling() != e) { // dokud se nedostanu ke znacce, pridavam uzly
            n.prependChild(n.previousSibling());
        }
    }
    p.unwrap(); // vymazani puvodniho "problemoveho rodice"
}
/**
 * ma testovaci element rodice, ktereho kontrolni element nema?
 *
 * @param e testovaci element
 * @param c kontrolni element
 * @return testovaci element ma unikatniho rodice (takoveho, ktery kontrolni
 * element nema)
 */
private boolean hasUniqueParent(Element e, Element c) {
    if (e.parents().isEmpty() || c.parents().isEmpty()) { // test na null
        return false;
    }
    for (Element pe : e.parents()) {
        if (!c.parents().contains(pe)) {
            return true; // unikatni rodic
        }
    }
    return false; // bez unikatniho rodice
}