Подтвердить что ты не робот

Сохраните текст выделения и покажите его позже в html и javascript

У меня сложная ситуация с html и javascript. Моя страница html позволяет пользователю выбирать текст и выделять его цветами. Теперь я хочу сохранить состояние в базе данных, чтобы показать его позже для этого пользователя. Конечно, я могу сохранить весь html после того, как пользователь отредактировал его. Но я просто хочу сохранить некоторые параметры, в сочетании с оригинальным html, чтобы показать страницу в состоянии пользователя в последний раз. Мы можем использовать эту функцию:

var index = innerHTML.indexOf(text);

чтобы выделить текст в этом индексе. Но в случае, если на странице есть много одинаковых текстов, я хочу выделить именно слова пользователя, выделенного им раньше.

Кто-нибудь может научить меня, как это сделать с помощью javascript?

Я очень ценю вашу помощь.

4b9b3361

Ответ 1

Range объекты и document.execCommand позволяют легко манипулировать выбором. Основная проблема в вашем случае - сохранение объекта диапазона в текстовом формате.

В основном вам нужно получить startContainer, startOffset, endContainer и endOffset, которые являются значениями, необходимыми для создания объектов Range. Offsets являются номерами, поэтому это довольно просто. Контейнеры - это узлы, которые нельзя напрямую сохранить в виде строк, так что основная проблема. Одна вещь, которую вы можете сделать, это добавить ключи к вашему DOM и сохранить ключ. Но тогда, поскольку в диапазонах контейнеры являются текстовыми узлами, вам нужно сохранить индекс текста node. Что-то вроде этого должно позволять пометить DOM ключами, используя рекурсивную функцию:

function addKey(element) {
  if (element.children.length > 0) {
    Array.prototype.forEach.call(element.children, function(each, i) {
      each.dataset.key = key++;
      addKey(each)
    });
  }
};

addKey(document.body);

Как только это будет сделано, вы можете преобразовать объекты диапазона в объект, который можно сохранить в виде строки. Вот так:

function rangeToObj(range) {
  return {
    startKey: range.startContainer.parentNode.dataset.key,
    startTextIndex: Array.prototype.indexOf.call(range.startContainer.parentNode.childNodes, range.startContainer),
    endKey: range.endContainer.parentNode.dataset.key,
    endTextIndex: Array.prototype.indexOf.call(range.endContainer.parentNode.childNodes, range.endContainer),
    startOffset: range.startOffset,
    endOffset: range.endOffset
  }
}

Используя это, вы можете сохранить каждый выбор, который пользователь создает для массива. Вот так:

document.getElementById('textToSelect').addEventListener('mouseup', function(e) {
  if (confirm('highlight?')) {
    var range = document.getSelection().getRangeAt(0);
    selectArray.push(rangeToObj(range));
    document.execCommand('hiliteColor', false, 'yellow')
  }
});

Чтобы сохранить основные моменты, вы сохраняете каждый объект в JSON. Чтобы проверить это, вы можете просто получить строку JSON из массива объектов диапазона. Как это (это используется кнопка get Seletion вверху):

document.getElementById('getSelectionString').addEventListener('click', function() {
  alert('Copy string to save selections: ' + JSON.stringify(selectArray));
});

Затем при загрузке пустого HTML вы можете использовать обратную функцию, которая будет создавать диапазоны из объектов, сохраненных в JSON. Вот так:

function objToRange(rangeStr) {
  range = document.createRange();
  range.setStart(document.querySelector('[data-key="' + rangeStr.startKey + '"]').childNodes[rangeStr.startTextIndex], rangeStr.startOffset);
  range.setEnd(document.querySelector('[data-key="' + rangeStr.endKey + '"]').childNodes[rangeStr.endTextIndex], rangeStr.endOffset);
  return range;
}

Итак, у вас может быть массив диапазонов в строках, которые вы конвертируете в объекты, а затем конвертируете в объекты Range, которые вы можете добавить. Затем, используя execCommand, вы устанавливаете некоторое форматирование. Как это (это используется кнопка выбора набора вверху, вы делаете это после обновления скрипка):

document.getElementById('setSelection').addEventListener('click', function() {
  var selStr = prompt('Paste string');
  var selArr = JSON.parse(selStr);
  var sel = getSelection();
  selArr.forEach(function(each) {
    sel.removeAllRanges();
    sel.addRange(objToRange(each));
    document.execCommand('hiliteColor', false, 'yellow')
  })
});

Смотрите: https://jsfiddle.net/sek4tr2f/3/

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

Ответ 2

Чтобы узнать его местоположение, вам нужно записать путь к node.
Это можно сделать несколькими способами.
Самый простой способ - переместить дом вверх до тела и создать селектор.

function getPathFromElement(element) {
    var stack = [];

    while (element.parentNode != document.documentElement) {
        var sibCount = 0;
        var sibIndex = 0;
        var childNodes = element.parentNode.childNodes;
        var childLength = childNodes.length;

        for (var i = 0; i < childLength; i++) {
            var sib = childNodes[i];

            if (sib.nodeName == element.nodeName) {
                if (sib === element) {
                    sibIndex = sibCount;
                }

                sibCount++;
            }
        }

        if (element.hasAttribute("id") && element.id !== "") {
            stack.unshift(`${element.nodeName.toLowerCase()}#${element.id}`);
        }
        else if (sibCount > 1) {
            stack.unshift(`${element.nodeName.toLowerCase()}:eq(${sibIndex})`);
        }
        else {
            stack.unshift(element.nodeName.toLowerCase());
        }

        element = element.parentNode;
    }

    return stack.join(" > ")
}

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

  • Простой текст на странице.
  • Выбор текста внутри текста ввода или текстового поля.

Для первого варианта вы можете использовать кнопку с обработчиком кликов или событием мыши.
Я использую кнопку для простоты.

function sendDataToServer(data) {
}

document.querySelector("#button").addEventListener("click", function (e) {
    var { target, text } = getSelectionTextAndContainerElement();

    var path = getPathFromElement(target);

    sendDataToServer({
        path: path,
        text: text 
    }); 
});

getSelectionTextAndContainerElement функция basicaly выбирает текст и элемент.

function getSelectionTextAndContainerElement() {
    var text;
    var containerElement = null;

    if (typeof window.getSelection !== "undefined") {
        var selection = window.getSelection();

        if (selection.rangeCount) {
            var node = selection.getRangeAt(0).commonAncestorContainer;
            containerElement = node.nodeType == 1 ? node : node.parentNode;
            text = selection.toString();
        }
    }
    else if (typeof document.selection !== "undefined" && document.selection.type !== "Control") {
        var textRange = document.selection.createRange();

        containerElement = textRange.parentElement();
        text = textRange.text;
    }

    return {
        text: text,
        target: containerElement
    };
}

Для второго варианта вы можете использовать обработчик события select.

document.addEventListener("select", onSelect, false);

function onSelect(e) {
    var { text } = getSelectionTextAndContainerElement();
    var path = getPathFromElement(e.target); 

    sendDataToServer({
        path: path,
        text: text 
    });       
}

Для ввода текста или textarea лучше использовать обработчик события select.
Если вы будете использовать первый вариант для выбора, вы не получите правильную цель node из-за того, что ввод текста и текстового поля построены с помощью Shadow DOM.
Поэтому лучше игнорировать цель node, полученную из функции getSelectionTextAndContainerElement, и использовать целевое свойство события select.

Я создал пример в jsfiddle для вас.

Ответ 3

моя идея состоит в том, чтобы добавить <span > в начало и конец выделенного текста после этого, когда вы сохраняете документ. Весь html сохраняется в базе данных, поэтому, когда он извлекает запись, выделенный текст остается.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>this is a paragraph creted to demonstrate highlighting selected text</p>
<script>
$(document).ready(function(){

$("p").on("mouseup",function() {
    oldtxt = chosenText();
    
        var newtxt = '<span  style="color:red;">' + oldtxt +'</span>';
$(this).html($(this).html().replace(oldtxt,newtxt));
    
});

//Grab selected text
function chosenText(){
    if(window.getSelection){
        return window.getSelection().toString();
    }
    else if(document.getSelection){
        return document.getSelection();
    }
    else if(document.selection){
        return document.selection.createRange().text;
    }
}
});
</script>

Ответ 4

Поскольку вы используете плагин для выделения текста, получите выделенные слова с помощью jQuery:

var words = $('.highlight').map(function() { return $(this).text(); });

Затем поместите их в массив

var saved = [ ];
for (var word in words) {
    if (-1 === saved.indexOf(word)) {
        saved.push(word);
    }
}

Наконец, вы можете сохранить их в базе данных. Плохой (но быстрый) способ сделать это - сохранить список как разделенный запятой, известный SQL antipattern:

var wordList = saved.join(',');

Когда вы извлекаете значение, вы разбиваете его на слова и для каждого слова вызывают плагин выделения.

Это не сработает, если какой-либо из текстов содержит запятую. В этом случае вам лучше сохранить каждое слово отдельно, что в конечном итоге избавит от нескольких других проблем, а не выяснит разделение символ, который "вряд ли" появится в тексте пользователя.

Ответ 5

Первый пример:

<textarea id="quote" cols="50" rows="5">
The above properties are especially useful in getting any user selected text from a form field where the indices of the selection isn't already known. The following demo echoes what the user has selected from a TEXTAREA using these properties:
</textarea>
 
<div id="output"></div>
 
<script>
 
var quotearea = document.getElementById('quote')
var output = document.getElementById('output')
quotearea.addEventListener('mouseup', function(){
    if (this.selectionStart != this.selectionEnd){ // check the user has selected some text inside field
        var selectedtext = this.value.substring(this.selectionStart, this.selectionEnd)
        output.innerHTML = selectedtext
    }
}, false)
 
</script>

Ответ 6

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

Ответ 7

Вы можете использовать метод .serialize(), который возвращает текстовую строку в стандартной кодировке с кодировкой URL. он выбрал индивидуальный элемент формы, такой как <input>, <textarea> и т.д. и нажмите сериализованную строку возврата в БД, используя $(form).serialize();, и для выделения изменений проверьте значение старого $(form).serialize(); с новым значением $(form).serialize();.

Ответ 8

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

Моим решением было бы сериализовать весь цветной html. затем создайте функцию scrub, чтобы удалить все цветовые блики и вернуться к базовому html. это позволяет html в db включать цветные блики, и редактирование все равно может происходить с сохранением выделенного выделения.

что-то вроде:

function unhighlight() {
  $('.highlighted').each(function(index, el) {
    $(el).replaceWith($(el).html());
  });
}

jsfiddle: https://jsfiddle.net/tobtedsc/5/