0

我发现了 CKEDITOR 选择对象的一些有用属性(通过editor.getSelection()):

e.getSelection().getRanges()[0].startOffset
e.getSelection().getRanges()[0].endOffset

这将返回所选文本的开始和结束位置 - 但仅与 startContainer(元素)和 endContainer(元素)有关。我想获得相对于整个文档或其他东西的绝对位置来识别所选的 html(开始或结束元素的一些实习生 id?)。我想在服务器端读取这个属性并在那里修改选定的 html。是否有一些通用方法可以将有关所选文本位置的信息从客户端移动到服务器端?

非常感谢。

4

4 回答 4

2

如果我理解正确,您想根据光标所在的位置或在提交/ajax 调用期间选择的位置来识别特定节点或多个节点服务器端。而且您已经知道获取当前选择的节点的咒语。

我建议在提交到服务器之前,手动标记元素。在提交之前向目标受害者添加自定义属性或类,然后使用该标签来识别节点服务器端。

例如[是选择的开始,]是结束,您的数据是:

<p>foo</p><p>[bar</p><p>baz]</p>

在提交之前,获取并用 JS 标记元素,使其看起来像

<p>foo</p><p class="chosen">bar</p><p class="chosen">baz</p>

然后您可以使用任何您喜欢的 XML/HTML 武器来查找标记的元素chosen(如果您使用的是 C#,我推荐使用 CSQuery)。然后只需删除标签并命令节点进行投标。对于多个节点,只需标记多个节点。

于 2013-04-23T20:15:51.327 回答
1

该计算强烈反对 CKEDITOR 的原则。但我认为,我发现了最好的解决方案——它几乎和 Nenotlep 建议的一样:

var bookmarks = e.getSelection().createBookmarks(true);
var startId = bookmarks[0].startNode;
var endId = bookmarks[0].endNode;

这将插入到代码中的不可见书签(ID 为 startId 和 endId 的跨度),然后我可以在服务器端处理它。现在我必须解决多选问题和标签交叉问题。

于 2013-04-24T08:15:34.813 回答
0

我找到了一个很好的优雅的解决方案:

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
}
于 2013-04-27T12:06:13.257 回答
0

现在我必须解决这个问题: - 如果有人想选择(* = 选择边框)这个:<b>some te*xt </b> aaa <i>bb*b</i>

... JS 把这个书签:

<b>some te
<span id="cke_bm_69S" style="display: none;">&nbsp;</span>
xt</b> 
aaa 
<i>bb
<span id="cke_bm_69S" style="display: none;">&nbsp;</span>
b</i> 

...我必须做这样的事情:

<b>some te
</b><span property="..."><b>xt</b>
aaa 
<i>bb</i></span><i>b</i> 

我的解决方案(尚未测试):选择一个 id="cke_bm_69S" 的节点,确保标签交叉问题,如果有一些则使用 before() 和 after() 方法插入 html。但是恐怕会有临时未关闭的标签,我不知道是否可以。

于 2013-04-25T10:07:13.207 回答