...但是我不确定是否有
点击特定单词的方式(一个单词中可能有多个单词
句子),还有没有办法设置建议框的样式并添加
通过单击它们可以选择的单词列表。
将您的问题分解为多个步骤。这将使您更容易理解问题域并设计解决方案。根据我对您的问题的了解,大致的步骤及其实施可能类似于下面描述的那些。
注意 1:答案基于纯 JavaScript。请记住,像 jQuery 等所有框架都是 JavaScript 只是在更高层次上抽象出来的。首先学习基本的 JavaScript 很重要。
注意 2:我在整个答案中以嵌入式链接的形式提供了关键概念的参考(供您获取更多信息和学习)。
1) 单词的标记和 Javascript 设置: 某些单词有同义词。同义词在标题属性中的标记本身中可用。你到达的标记很好:
标记:
<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 返回一个节点列表,因此将其转换为数组的最简单方法是调用slice on array prototype。我们需要一个数组,以便我们以后可以对其进行迭代。
2) 菜单的标记和 Javascript 设置: 需要弹出一个建议框。因此,只需添加一个包含同义词的元素。你已经知道会有一个同义词列表,所以从语义上讲,有一个列表元素是有意义的。并给它一个id。我们稍后会动态填充它。
标记:
<ul id="synonymMenu"></ul>
Javascript:
var menu = document.getElementById('synonymMenu');
3) 建议框菜单应弹出:每当点击这样的词。因此,我们需要在所有将监听点击事件的单词上添加事件监听器。在上面的第一步中,我们已经在变量words 中找到了单词。我们只是迭代和add the event listener 来执行函数manageMenu。我们稍后会定义该函数。在此过程中,我们还将现有单词缓存在数据属性中,以便以后使用setAttribute。
words.forEach(function(wrd) {
wrd.setAttribute('data-word', wrd.textContent);
wrd.addEventListener('click', manageMenu);
});
4) 用选定的同义词替换单词:每当在建议框菜单中单击同义词时。所以我们还必须在同义词列表中添加一个点击事件监听器。在上面的步骤 2 中,我们已经将菜单存储在变量 menu 中。只需添加侦听器即可执行函数applySynonym。我们稍后会定义该函数。
menu.addEventListener('click', applySynonym);
5) 我们还必须关闭悬空的建议框:我们可以通过单击主体上的任意位置来实现。只需在正文上添加另一个单击事件处理程序。使用hide 参数执行函数toggleMenu。稍后会定义这个函数。
document.body.addEventListener('click', function() {
toggleMenu('hide');
});
6) 从 title 属性创建同义词列表并显示:它在建议框菜单中,当点击单词时。我们将在步骤 3 中声明的 manageMenu 函数中定义。解释在代码 cmets 中。
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();
}
上面代码中对你的关键引用是关于using the event object and its target、getting the position of word relative to viewport、createElement、appendChild和stopPropagation
7) 同义词应附加到原始单词:并在单击同义词后显示在其位置。我们将在第 4 步中引用的 applySynonym 函数中定义它。
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's 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>
编辑:
根据 Op 的 cmets,代码已更新,以适应通过复选框选择多个同义词。增加的复杂性在于添加复选框而不是普通的锚点,更改事件侦听器以获得相同的更新样式,以及在重复点击时保留预先存在的选择的逻辑。
更新的小提琴:https://jsfiddle.net/abhitalks/ffpL4f7k/
更新片段:
(function() {
var menu = document.getElementById('synonymMenu'),
menuWrap = document.getElementById('menuWrapper'),
okButton = document.getElementById('synOk'),
words = [].slice.call(document.querySelectorAll('i.synonyms')),
currentWord = null
;
words.forEach(function(wrd) {
wrd.setAttribute('data-word', wrd.textContent);
wrd.addEventListener('click', manageMenu);
});
okButton.addEventListener('click', applySynonym);
document.body.addEventListener('click', function(e) {
if (isDescendant(menuWrapper, e.target)) {
return;
}
toggleMenu('hide');
});
function manageMenu(e) {
var synonyms, opt, lbl, chk, txt, position, existing;
clearMenu(); toggleMenu('show');
currentWord = e.target;
position = currentWord.getBoundingClientRect();
menuWrap.style.top = position.top + 20 + 'px';
menuWrap.style.left = position.left + 2 + 'px';
existing = currentWord.textContent;
synonyms = currentWord.getAttribute('title').split('|');
synonyms.forEach(function(syn) {
opt = document.createElement('li');
lbl = document.createElement('label');
chk = document.createElement('input');
chk.setAttribute('type', 'checkbox');
txt = document.createTextNode(syn);
lbl.appendChild(chk);
lbl.appendChild(txt);
opt.appendChild(lbl);
menu.appendChild(opt);
});
preSelect(existing);
e.stopPropagation();
}
function preSelect(existing) {
var labels = [].slice.call(menu.querySelectorAll('label'));
labels.forEach(function(lbl) {
if (existing.indexOf(lbl.textContent) > -1) {
lbl.firstChild.checked = true;
}
});
}
function applySynonym(e) {
var txt = '', labels, checked, selected;
labels = [].slice.call(menu.querySelectorAll('label'));
checked = labels.filter(function(lbl){
return lbl.firstChild.checked;
});
selected = checked.map(function(lbl){
return lbl.textContent;
}).join('|');
txt += '{' + currentWord.getAttribute('data-word') + '|';
txt += selected + '}';
currentWord.textContent = txt;
toggleMenu('hide');
e.stopPropagation();
}
function toggleMenu(mode) {
if (mode == 'show') { menuWrap.style.display = 'block'; }
if (mode == 'hide') { menuWrap.style.display = 'none'; }
}
function clearMenu() {
while (menu.hasChildNodes()) {
menu.removeChild(menu.lastChild);
}
}
function isDescendant(parent, child) {
var node = child.parentNode;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
})();
* { font-family: sans-serif; box-sizing: border-box; }
html, body { height: 100%; }
div.wrap {
border: 1px solid #ddd; max-height: 480px;
padding: 4px 22px 4px 4px; font-size: 0.9em;
overflow: hidden; overflow-y: auto;
}
i.synonyms { cursor: pointer; color: #333; }
div#menuWrapper {
position: absolute; display: none; width: 128px;
padding: 4px; margin: 0;
border: 1px solid #bbb; background-color: #efefef;
box-shadow: 0px 0px 6px 1px rgba(128,128,128,0.3);
}
ul#synonymMenu {
max-height: 120px;
overflow: hidden; overflow-y: auto;
list-style: none; padding: 0; margin: 0;
}
ul#synonymMenu > li { display: block; }
ul#synonymMenu label {
display: block; color: #333; font-size: 0.9em;
padding: 2px 18px 2px 4px;
}
ul#synonymMenu label:hover { background-color: #99b; }
button#synOk { padding: 2px; width: 100%; }
<div class="wrap">
<p>
I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges.
</p>
<p>
I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges.
</p>
<p>
I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. I <i class="synonyms" title="love|relish|savor">like</i> apples. I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges.
</p>
</div>
<div id="menuWrapper">
<ul id="synonymMenu"></ul>
<hr/>
<button id="synOk">Ok</button>
</div>