Возвращает индексную позицию ссылок в абзаце

#javascript #html

#javascript #HTML

Вопрос:

Я хочу получить массив всех ссылок из абзаца innerHTML и сохранить их начальный и конечный индексы вместе с содержимым в сравнении с textContent.

 <p id ="1">This is a <a href ="/hey">Link</a> and this is also a <a href="/hey">Link</a></p>
  

Итак, для этого я пытаюсь получить что-то вроде:

 //I know this is formatted weird, just showing an example of what data would like at end.
links = [
  'link1': { start_index: 11, end_index: 14, href: '/hey, text: 'Link'},
  'link2': { start_index: 35, end_index: 38, href: '/hey, text: 'Link'},
]
  

Начальный индекс и конечный индекс должны основываться на расположении ссылок в текстовом контенте абзаца.

Я пытался использовать indexOf ()

 str = document.getElementById("1").innerHTML;
var index_start = str.indexOf("<a href ="/hey">Link</a>");
  

Но это просто вернет индекс ссылки в innerHTML, я не уверен, как получить ее местоположение в textContent или как получить местоположение всех ссылок.

Спасибо и извините, если это сбивает с толку.

Комментарии:

1. Каков здесь вариант использования более высокого уровня? Подумайте, что могут быть другие встроенные элементы, содержащие текст, также подобный <span> или <strong> . Если есть, что учитывается?

2. Содержимое всегда будет иметь только hrefs и предназначено для отправки данных на серверную часть, где каждая ссылка расположена в открытом тексте, чтобы PHP мог затем манипулировать ими, используя начальный и конечный индексы.

3. Таким образом, вы говорите, что никогда не будет других встроенных элементов перед <a> ? Все еще не очень понятно, для чего вам это нужно

4. хм! Это становится сложнее, когда есть две ссылки в одном p

5. @AlwaysHelping Это обязательно делает!!

Ответ №1:

Это было немного сложно сделать, но я, наконец, сделал это.

Следующий код сработал для меня, с вашим примером.

 const str = document.getElementById('1')

const linksInStr = [...str.querySelectorAll('a')]

let strInnerHTML = str.innerHTML

const links = linksInStr.map((element) => {
  const urlProps = new URL(element.href)
  const el = `<a href="${urlProps.pathname}">${element.textContent}</a>`
  const start_index = strInnerHTML.indexOf(el)
  const end_index = start_index   element.textContent.length
  strInnerHTML = strInnerHTML.replace(el, element.textContent)

  return { start_index, end_index, href: urlProps.pathname, text: element.textContent }
})

console.log(links)  
 <p id="1">This is a <a href="/hey">Link</a> and this is also a <a href="/hey2">Link2</a></p>  

Пожалуйста, обратите внимание, что мне пришлось изменить href и textContent второго элемента «a», добавив «2», потому что метод «indexOf» возвращает индекс только первого текста, который соответствует запросу.

Комментарии:

1. Выглядит хорошо! Единственная проблема, которую я вижу, заключается в том, что если текст ссылки существует в другом месте текста абзаца, то получение indexOf может быть отключено.

2. Эй, я немного изменил код, чтобы избежать этой проблемы. Теперь он соответствует только точному элементу «a» innerHTML вместо его textContent, чтобы избежать проблемы с методом indexOf.

3. Я думаю, что это становится еще сложнее, используя длину html, чтобы собрать его обратно позже. Не пытаюсь выделить ваши ссылки отдельно, потому что это интересное упражнение и ясно видно, что вы приложили хорошие усилия к решению. Хорошая работа

4. @N-bred Wao. Очень хороший вариант. Я собирался применить похожее решение, но был чем-то занят!

Ответ №2:

Вот исходное концептуальное решение.

Он создает клон <p> затем заменяет ссылки в этом клоне заполнителем || , который, в свою очередь, позволяет разбить текстовую строку на массив, используя этот заполнитель.

Затем он отображает ссылки в массив объектов и берет предыдущую длину текста из массива разделенного текста (пока не из начала абзаца, а из предыдущей ссылки).

При немного большей доработке должно быть довольно легко перепроектировать это, чтобы поместить ссылки обратно в текст

 const p = document.querySelector('p');

const txtArray = getTextInArray(p)

const links = Array.from(p.querySelectorAll('a')).map((el, i) => {
  return {
    href: el.href,
    linkTxt: el.textContent,
    prevTxtLength: txtArray[i].length
  }
})

console.log(links)


function getTextInArray(p) {
  let clone = p.cloneNode(true);
  clone.querySelectorAll('a').forEach(a => a.replaceWith('||'));
  return clone.textContent.split('||');
}  
 <p>Some text <a href="foo">Foo</a> some more text <a href="boo">Boo</a></p>  

Комментарии:

1. Так это в основном для скребка?

2. Очень простой текстовый редактор, который не позволяет использовать форматированный текст, кроме ссылок, а затем данные, хранящиеся в json, для чего-либо еще. Но не хотел затруднять пользователей вводом bbcode / wikitext / markdown, поэтому просто кнопка, которая добавляет ссылку на абзац в вашей позиции курсора.

3. Ну, может быть, моя идея о том, чтобы поместить текст в массив, также является способом его сохранения. Упрощает создание ссылки обратно, если она уже сохранена в array

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

5. @charlietfl Отличный друг!