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

Как создать поле предложений (окно подсказки в Javascript) Внутри ящика с текстом

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

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

Код html приведен ниже:

Original:
I <b class="synonyms" style="color:black;" 
title="love|really like|really love">like</b> apples.

The result should be(after a user chooses synonyms):
I <b>{like|love}</b> apples.
4b9b3361

Ответ 1

... однако я не уверен, есть ли способ щелкнуть по определенному слову (может быть более одного слова в предложение), а также способ стилизации окна предложения и добавления список слов на выбор, нажав на них.

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

Примечание 1. Ответ основан на чистом JavaScript. Помните, что все фреймворки, такие как jQuery и т.д., JavaScript только абстрагируются на более высоком уровне. Для вас важно сначала изучить базовый JavaScript.

Примечание 2. Я предоставил ссылки на ключевые понятия (для получения дополнительной информации и обучения) в виде встроенных ссылок на протяжении всего этого ответа.

1) Настройка разметки и Javascript для слов: Есть определенные слова, которые имеют синонимы. Синонимы доступны в самой разметке в атрибуте title. Разметка, в которую вы попали, очень хороша:

Разметка:

<p>
    I <i class="synonyms" title="love|really like|really love">like</i> apples. 
    I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. 
</p>

Нам нужно уметь идентифицировать все слова, несущие синонимы, чтобы мы могли использовать Javascript для их управления. Они идентифицируются в разметке как элементы i с классом synonyms.

JavaScript:

var words = [].slice.call(document.querySelectorAll('i.synonyms'));

querySelectorAll возвращает список node, поэтому самым простым способом преобразования этого в массив является вызов фрагмента на прототипе массива. Нам нужен массив, чтобы потом продолжить его.

2) Настройка разметки и Javascript для меню: Должно появиться окно с предложением. Итак, просто добавьте элемент, который будет содержать ваши синонимы. Вы уже знаете, что будет список синонимов, поэтому семантически имеет смысл иметь элемент списка. И дайте ему идентификатор. Мы будем заполнять его динамически позже.

Разметка:

<ul id="synonymMenu"></ul>

JavaScript:

var menu = document.getElementById('synonymMenu');

3) В окне меню подсказки должно появляться всплывающее окно: всякий раз, когда нажимается такое слово. Итак, нам нужно добавить и прослушиватель событий на все такие слова, которые будут прослушивать событие click. У нас уже есть слова в переменной words на первом шаге. Мы просто повторяем и добавляем прослушиватель событий для выполнения функции manageMenu. Мы определим эту функцию позже. Пока мы находимся в этом, мы также кэшируем существующее слово в атрибуте данных, чтобы использовать его позже, используя setAttribute.

words.forEach(function(wrd) {
    wrd.setAttribute('data-word', wrd.textContent);
    wrd.addEventListener('click', manageMenu);
});

4) Заменить слово выбранным синонимом: всякий раз, когда в окне подсказки отображается синоним. Поэтому мы должны добавить прослушиватель событий щелчка в список синонимов. У нас уже есть меню, сохраненное в переменной menu на шаге 2 выше. Просто добавьте слушателя для выполнения функции applySynonym. Мы определим эту функцию позже.

menu.addEventListener('click', applySynonym);

5) Мы также должны закрыть окно с болтающимся предложением. Мы могли бы сделать это, щелкнув в любом месте тела. Просто добавьте еще один обработчик события click на теле. Выполнить функцию toggleMenu с параметром hide. Определит эту функцию позже.

document.body.addEventListener('click', function() {
    toggleMenu('hide');
});

6) Создайте список синонимов из атрибута title и покажите: он находится в меню окна предложений, когда нажимается слово. Это мы определим в функции manageMenu, которую мы объявили на шаге 3. Объяснение содержится в комментариях кода.

function manageMenu(e) {
    // define variables
    var synonyms, optn, link, position;  

    // clear existing list and then show the menu
    clearMenu(); toggleMenu('show'); 

    // cache the click event target to a variable to be used later
    currentWord = e.target;

    // get the position of word relative to viewport
    position = currentWord.getBoundingClientRect();

    // use that position to shift the popup menu near to the word
    menu.style.top = position.top + 24 + 'px';
    menu.style.left = position.left + 2 + 'px';

    // extract title attribute, split by | and store in array
    synonyms = currentWord.getAttribute('title').split('|');

    // iterate array creating an li and anchor for each synonym
    // createElement creates a new element
    // appendChild adds an element to another
    synonyms.forEach(function(syn) {
        optn = document.createElement('li');
        link = document.createElement('a');
        link.setAttribute('href', '#'); link.textContent = syn;
        // add anchor to li, and the li to the menu
        optn.appendChild(link); menu.appendChild(optn);
    });
    // stop propagation of click, so that it doesn't go to body
    e.stopPropagation(); 
}

Ключевыми ссылками для вас в приведенном выше коде являются с использованием объекта события и его цели, чтобы получить позицию слово относительно видового экрана, createElement, appendChild и stopPropagation

7) Синоним должен быть добавлен к исходному слову: и показан на его месте, после щелчка синонима. Это мы определим в applySynonym fucntion, на который мы ссылались на шаге 4.

function applySynonym(e) {
    var txt = '';

    // Because we added event listener to the parent ul element, 
    // we have to check if the clicked element is the anchor or not
    if (e.target.tagName != 'A') { return false; }

    // We retrieve the orginal text from the data attribute, 
    // which we cached in step 6 above. And append current anchor text
    txt += '{' + currentWord.getAttribute('data-word') + '|';
    txt += e.target.textContent + '}';
    // replace the text of the word
    currentWord.textContent = txt;
    toggleMenu('hide'); // hide the suggestion box menu
    // stop propagation of click, so that it doesn't go to body
    // prevent default so that clicking anchor doesn't jump to top
    e.stopPropagation(); e.preventDefault();
}

Ключевыми ссылками для вас в приведенном выше коде являются preventDefault.

8) Мы определяем остальные вспомогательные функции:

function toggleMenu(mode) {
    if (mode == 'show') { menu.style.display = 'block'; }
    if (mode == 'hide') { menu.style.display = 'none'; }
}

function clearMenu() {
    // we loop the child nodes of menu ul element, 
    // remove the last child (last li) of that ul element, 
    // until it does not has-child-nodes.
    while (menu.hasChildNodes()) { 
        menu.removeChild(menu.lastChild); 
    }
}

Ключевыми ссылками для вас в приведенном выше коде являются hasChildNodes, removeChild, и lastChild.

9) Определите презентацию с помощью CSS, особенно сделав меню абсолютно неподвижным, скрывая его при первой загрузке, а также украсьте презентацию:

ul#synonymMenu {
    position: absolute; display: none;
    ...
    border: 1px solid #bbb; background-color: #efefef;
}

10) Тест.

Демо-скрипт: https://jsfiddle.net/abhitalks/zske2aoh/

Демо-фрагмент:

(function() {
	var menu = document.getElementById('synonymMenu'), 
		words = [].slice.call(document.querySelectorAll('i.synonyms')), 
		currentWord = null
	;
	
	words.forEach(function(wrd) {
		wrd.setAttribute('data-word', wrd.textContent);
		wrd.addEventListener('click', manageMenu);
	});
	menu.addEventListener('click', applySynonym);
	document.body.addEventListener('click', function() {
		toggleMenu('hide');
	});

	function manageMenu(e) {
		var synonyms, optn, link, position; 
		clearMenu(); toggleMenu('show'); 
		currentWord = e.target;
		position = currentWord.getBoundingClientRect();
		menu.style.top = position.top + 24 + 'px';
		menu.style.left = position.left + 2 + 'px';
		synonyms = currentWord.getAttribute('title').split('|');
		synonyms.forEach(function(syn) {
			optn = document.createElement('li');
			link = document.createElement('a');
			link.setAttribute('href', '#'); link.textContent = syn;
			optn.appendChild(link); menu.appendChild(optn);
		});
		e.stopPropagation();
	}
	
	function applySynonym(e) {
		var txt = '';
		if (e.target.tagName != 'A') { return false; }
		txt += '{' + currentWord.getAttribute('data-word') + '|';
		txt += e.target.textContent + '}';
		currentWord.textContent = txt;
		toggleMenu('hide');
		e.stopPropagation(); e.preventDefault();
	}
	
	function toggleMenu(mode) {
		if (mode == 'show') { menu.style.display = 'block'; }
		if (mode == 'hide') { menu.style.display = 'none'; }
	}
	
	function clearMenu() {
		while (menu.hasChildNodes()) { 
			menu.removeChild(menu.lastChild); 
		}
	}
	
})();
* { font-family: sans-serif; }
html, body { height: 100%; }
i.synonyms { cursor: pointer; color: #333; }
ul#synonymMenu {
	position: absolute; display: none;
	width: auto; max-height: 120px; 
	overflow: hidden; overflow-y: auto;
	list-style: none; padding: 0; margin: 0; 
	border: 1px solid #bbb; background-color: #efefef;
	box-shadow: 0px 0px 6px 1px rgba(128,128,128,0.3);
}
ul#synonymMenu > li { display: block; }
ul#synonymMenu a { 
	display: block; padding: 4px 20px 4px 6px; 
	color: #333; font-size: 0.9em; text-decoration: none;
}
ul#synonymMenu a:hover {
	background-color: #99b;
}
<p>
I <i class="synonyms" title="love|really like|really love">like</i> apples. 
I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. 
</p>
<ul id="synonymMenu"></ul>

Ответ 2

Я построю следующий компонент jQuery, который соответствует вашим потребностям, я полагаю. Здесь jsbin, если вы этого предпочтете.

//jquery component
$.fn.synonyms = function(options){
  options = $.extend({}, {separator: '|'}, options);
  this.each(function(elKey, el){
    var $el = $(el),
        originalText = $el.text(),
        originalTextSpan = $('<span>'+originalText+'</span>');
    $el.html(originalTextSpan);
    var suggestionBox = '<div>';
    $.each($el.attr('data-synonyms').split(options.separator),
           function(key, suggestion){
      suggestionBox+='<span>'+suggestion+'</span> - ';
    }
          );
    suggestionBox = suggestionBox.slice(0, -2);
    suggestionBox += '</div>';
    suggestionBox = $(suggestionBox);
    suggestionBox.css({
      display: 'none'
    });
  
    $el.click(function(){
      suggestionBox.toggle();
    });
  
    suggestionBox.on('click','span',function(){
      var selectedText = $(this).text();
      originalTextSpan.text('{'+originalText+'|'+selectedText+'}');
      onSelected(selectedText);
    });
  
    $el.append(suggestionBox);
  });
  
  
  function onSelected(selectedText){
    if(options.onSelected){
      options.onSelected(selectedText);
    }
  }
};


// How to use the component
$(function(){
  $('[data-synonyms]').synonyms({
    onSelected: function(selectedText){
      alert('you selected:'+selectedText);
    }
  });
});
div[data-synonyms]{
  display: inline;
  position: relative;
  cursor: pointer;
  text-decoration: underline;
}
div[data-synonyms] > div{
  white-space: nowrap;
  position: absolute;
  top: 1.2em;
  left: 0;
  background: #fff;
  border: 1px solid #bbb;
  padding: 2px;
}
div[data-synonyms] > div > span{
  text-decoration: underline;
  cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  I <div data-synonyms="love|really like|really love">like</div> apples. Carrots are the <div data-synonyms="worst|orangest">best</div> though.
</body>
</html>

Ответ 3

$(document).ready(function() {
  $("b").on("click", function() {
    var $b = $(this),
      alternatives = this.title.split('|').join('</option><option>');
    $b.after('<select class="selector"><option>&nbsp;</option><option>' + alternatives + '</option><select>');
  });
  $("body").on("change", "select.selector", function() {
    var $sl = $(this),
      txt = $sl.val();
    $sl.prev('b').text(txt);
    $sl.remove();
  });
});
b {
  color: red;
  cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="text">
  <p>I <b title="like|love|adore">like</b> apples, <b title="especially|particularily">particularily</b> if they are <b title="fresh|ripe">ripe</b>.</p>

</div>

Ответ 4

Я создал другое решение, используя раскрывающееся меню bootstrap:

http://www.bootply.com/rWfTgSwf1z

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

Вы можете иметь предложения как следующие, где синонимы для слов определены в атрибуте data-synonyms:

<div>
  I
  <span data-synonyms="love|really like|really love">like</span>
  apples and
  <span data-synonyms="mangoes|bananas|other fruits">oranges</span>.
</div>

Затем в javascript мы создаем выпадающие списки и заменяем существующие элементы:

$('[data-synonyms]').each(function () {

  // get the current element
  var $this = $(this);

  // create a dropdown wrapper
  var $dropdownDiv = $('<div>').addClass('dropdown word-dropdown');

  // create the dropdown trigger
  var $a = $('<a>').attr('data-toggle', 'dropdown').text($this.text()).appendTo($dropdownDiv);

  // create the dropdown list
  var $ul = $('<ul>').addClass('dropdown-menu').appendTo($dropdownDiv);

  // get the synonyms and append the existing word
  var synonyms = $this.attr('data-synonyms').split('|');
  synonyms.splice(0, 0, $this.text());

  // create an entry in the dropdown for each synonym
  $.each(synonyms, function (idx, syn) {
    var $li = $('<li>').addClass('synonyms').appendTo($ul).append($('<a>').text(syn.trim()));

    // add a handler which replaces the existing word with the synonym
    $li.on('click', function () {
        $a.text(syn.trim());
    });
  });

  // replace the current element with the dropdown element
  $this.replaceWith($dropdownDiv);

  // activate the dropdown
  $a.dropdown();

});

Ответ 5

Здесь скрипка

Я использовал jQuery и немного изменил синтаксис. Каждый вариант должен быть span с классом selectable. Затем ему нужен атрибут options. Опции представлены так, как вы предлагали.

Мой скрипт отличается от других тем, что в нем не указан вариант, который уже выбран, и его можно использовать с клавиатурой (попробуйте использовать клавиши Tab, Enter и клавиши со стрелками).

$(function () {
  $('.selectable').each(function () {
    var $this = $(this)
    var list  = $this.attr('options').split('|')
    var text  = $this.text()
    $this
      .data('original', text)
      .html('<div><span>' + text + '</span><ul><li tabindex="0">' + list.join('</li><li tabindex="0">') + '</li></ul></div>')
  }).on('mousedown', function (e) {
    e.preventDefault()
    var $this   = $(this)
    var $target = $(e.target)
    var $focus  = $this.find(':focus')
    if ($focus.length) $focus.blur()
    else $this.find('li:not(.active)').eq(0).focus()
    if ($target.is('li')) changeSelection($this, $target)
  }).on('keydown', function (e) {
    var which = e.which
    if (which === 13) changeSelection($(this))
    else if (which === 40) $(this).find(':focus').next().focus()
    else if (which === 38) $(this).find(':focus').prev().focus()
  })
  function changeSelection ($this, $target) {
    $target = $target || $this.find(':focus')
    $target.blur()
    $this
      .one('transitionend', function () {
        $target.addClass('active').siblings().removeClass('active').last().focus().blur()
      })
      .addClass('filled')
      .find('span').text($this.data('original') + ' | ' + $target.text())
  }
})
body {
  font-family: sans-serif;
}

.selectable {
  cursor: default;
}

.selectable:focus {
  outline: none;
}

.selectable div {
  position: relative;
  display: inline-block;
}

.selectable::before,
.selectable::after,
.selectable span {
  color: #F00;
}

.selectable.filled::before {
  content: '{';
  margin-right: .2em;
}

.selectable.filled::after {
  content: '}';
  margin-left: .2em;
}

.selectable ul {
  position: absolute;
  top: calc(100% + .2em);
  left: calc(-.4em - 1px);
  list-style: none;
  margin: 0;
  padding: 1px 0 0;
  overflow: hidden;
  background-color: #DDD;
  z-index: 1;
}

.selectable ul:not(:focus-within) {
  pointer-events: none;
  user-select: none;
  opacity: 0;
  transform: translateY(-5px);
  transition: opacity .25s ease, transform .4s ease;
}

.selectable ul:focus-within {
  transition: opacity .25s ease, transform .4s ease -.15s;
}

.selectable li {
  white-space: nowrap;
  padding: .4em;
  margin: 0 1px 1px;
  background-color: #FFF;
}

.selectable li:hover {
  background-color: #F7F7F7;
}

.selectable li.active {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>I <span class="selectable" options="love|really like|really love">like</span> apples.</p>
<p>There nothing <span class="selectable" options="in this world|in the universe">on earth</span> I eat more than apples.</p>