Вставить ссылку в contenteditable элемент - PullRequest
17 голосов
/ 09 апреля 2011

Я работаю над простой системой блогов и использую contenteditable, чтобы пользователи могли форматировать текст.

До сих пор все работает как шарм.

Следующая вещь, которую яхочу, чтобы пользователи могли добавить гиперссылку в текст.

Пользователь должен выбрать (часть) текст и нажать кнопку ссылки.После этого открывается всплывающее окно, где пользователи должны ввести адрес ссылки.

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

Как можноЯ реализую эту функциональность, так как понятия не имею, как это сделать?

Мой сайт: http://82.170.147.49/blog/3/alpha-release

jsFiddle моего сайта: http://jsfiddle.net/qhN9j/

Ответы [ 5 ]

57 голосов
/ 11 апреля 2011

document.execCommand() делает это для вас во всех основных браузерах:

document.execCommand("CreateLink", false, "http://stackoverflow.com/");

Чтобы сохранить выбор при отображении диалогового окна ссылки, вы можете использовать следующие функции:

function saveSelection() {
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            var ranges = [];
            for (var i = 0, len = sel.rangeCount; i < len; ++i) {
                ranges.push(sel.getRangeAt(i));
            }
            return ranges;
        }
    } else if (document.selection && document.selection.createRange) {
        return document.selection.createRange();
    }
    return null;
}

function restoreSelection(savedSel) {
    if (savedSel) {
        if (window.getSelection) {
            sel = window.getSelection();
            sel.removeAllRanges();
            for (var i = 0, len = savedSel.length; i < len; ++i) {
                sel.addRange(savedSel[i]);
            }
        } else if (document.selection && savedSel.select) {
            savedSel.select();
        }
    }
}

Пример jsFiddle: http://jsfiddle.net/JRKwH/1/

ОБНОВЛЕНИЕ

Получить ссылки, созданные (если они вообще были созданы), довольно сложно.Вы можете использовать мою собственную библиотеку Rangy :

var sel = rangy.getSelection();
if (sel.rangeCount) {
    var links = sel.getRangeAt(0).getNodes([1], function(el) {
        return el.nodeName.toLowerCase() == "a";
    });
    alert(links.length);
}

... или что-то вроде следующего:

function getLinksInSelection() {
    var selectedLinks = [];
    var range, containerEl, links, linkRange;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            linkRange = document.createRange();
            for (var r = 0; r < sel.rangeCount; ++r) {
                range = sel.getRangeAt(r);
                containerEl = range.commonAncestorContainer;
                if (containerEl.nodeType != 1) {
                    containerEl = containerEl.parentNode;
                }
                if (containerEl.nodeName.toLowerCase() == "a") {
                    selectedLinks.push(containerEl);
                } else {
                    links = containerEl.getElementsByTagName("a");
                    for (var i = 0; i < links.length; ++i) {
                        linkRange.selectNodeContents(links[i]);
                        if (linkRange.compareBoundaryPoints(range.END_TO_START, range) < 1 && linkRange.compareBoundaryPoints(range.START_TO_END, range) > -1) {
                            selectedLinks.push(links[i]);
                        }
                    }
                }
            }
            linkRange.detach();
        }
    } else if (document.selection && document.selection.type != "Control") {
        range = document.selection.createRange();
        containerEl = range.parentElement();
        if (containerEl.nodeName.toLowerCase() == "a") {
            selectedLinks.push(containerEl);
        } else {
            links = containerEl.getElementsByTagName("a");
            linkRange = document.body.createTextRange();
            for (var i = 0; i < links.length; ++i) {
                linkRange.moveToElementText(links[i]);
                if (linkRange.compareEndPoints("StartToEnd", range) > -1 && linkRange.compareEndPoints("EndToStart", range) < 1) {
                    selectedLinks.push(links[i]);
                } 
            }
        }
    }
    return selectedLinks;
}

jsFiddle: http://jsfiddle.net/JRKwH/3/

7 голосов
/ 09 апреля 2011

Как сказал Альфред, уже есть хорошо разработанные редакторы, особенно для базовых функций.Вы можете ограничить его использование так мало или столько функций, сколько вы хотите.

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

var selected = document.getSelection();
document.execCommand("insertHTML",false,"<a href='"+href+"'>"+selected+"</a>");
6 голосов
/ 09 февраля 2016

Лучший ответ:

function link() {
  if (window.getSelection().toString()) {
    var a = document.createElement('a');
    a.href = 'http://www.google.com';
    a.title = 'GOOGLE';
    window.getSelection().getRangeAt(0).surroundContents(a);
  }
}
select some of text then click link button!
<button onclick='link()'>link text to google</button>

Этот метод может применяться везде, и для него не требуется, чтобы элемент был contenteidtable.

, к которому можно добавить любое событие или атрибуты.новый элемент A, как и другие элементы.

window.getSelection().toString() проверяет, выделен ли какой-либо текст.Он хорошо работает в Chrome, у меня нет IE для тестирования, в любом случае, есть другие способы проверить это.Но surroundContents(), который является ключевой частью, доступен в IE9, как предложено MDN.

Наконец, я предлагаю использовать iFrame вместо contenteditable div, чтобы не было беспокойства о сохранении выделения.

2 голосов
/ 03 августа 2015

EDIT Это невозможно в IE в Execcommand, потому что мы не можем вставить кавычки в 'href', мы должны сделать это в чистом JavaScript с диапазоном:

// IN DIV IN ONE IFRAME

// Get the frame
var iframe = document.getElementById('myframe');

// Selection object in the frame
theSelection = iframe.contentWindow.getSelection();

// position of the selection to insert
theRange = theSelection.getRangeAt(0);

// get content inside the original selection (and delete content in)
var fragment = theRange.extractContents();

// Create a new link in frame
var newLink = iframe.contentWindow.document.createElement('a');

// Create a text element with the fragment to put in the link
var theText = document.createTextNode(fragment.textContent);

// URL 
theLink.href = '#';

// Title
theLink.title = 'title';

// Attribute 'onclick'
theLink.setAttribute('onclick', thelink);

// Target
theLink.target = '_blank';

// Add the text in the link
theLink.appendChild(theText);

// Insert the link at the range
theRange.insertNode(newLink);

// DIV БЕЗ КАДРОВ

// Selection object in the window
theSelection = window.getSelection();

// begin of the selection to insert
theRange = theSelection.getRangeAt(0);

// get content inside the original selection (and delete content in)
var fragment = theRange.extractContents();

// Create a new link in the document
var newLink = document.createElement('a');

// Create a text element with the fragment to put in the link
var theText = document.createTextNode(fragment.textContent);

// URL 
theLink.href = '#';

// Title
theLink.title = 'title';

// Attribute 'onclick'
theLink.setAttribute('onclick', thelink);

// Target
theLink.target = '_blank';

// Add the text in the link
theLink.appendChild(theText);

// Insert the link at the range
theRange.insertNode(newLink);
0 голосов
/ 08 февраля 2016

Я бы сделал это следующим образом:

  1. Создать ссылку с (возможно, уникальным) начальным фиктивным атрибутом href, чтобы идентифицировать его по.
  2. Получить этот элемент с помощью document.querySelector('a[href=<unique-href>]').
  3. Теперь у вас есть ссылка на созданный элемент, и вы можете делать с ней по своему усмотрению.

Преимущество этого в том, что вам не нужно работать сSelection вообще.

...