首页 > 解决方案 > JavaScript 和 css:突出显示具有相同背景的多个单词

问题描述

我正在尝试制作一个基本的 Web 应用程序,人们可以在其中突出显示多个单词(即,单击第一个单词,然后再单击一个单词,所有内容都会被突出显示,甚至在另一行上)。

到目前为止,我能够将所有单词包装在标签中,并为每个单词设置一个单击事件侦听器,将其 className 更改为“highlight”,即background-color:yellow.

问题是仅突出显示单个单词的背景,但我希望突出显示两个(或更多,甚至在不同行)单词之间的所有内容。

为了使事情更复杂一点,我在单词之间还有标点符号和其他东西,它们没有被跨度标签包围,但我希望单词之间的所有内容都具有不同的背景颜色/被选中。

我正在考虑将选择的必要单词放在它们自己的单独的跨度标签中,但是,我不确定如何使其动态准确地更改,我还希望用户保存选择然后用按钮或其他东西重新选择它们,这意味着一个词可以在两个不同的短语中,我不确定一个词如何在两个不同的跨度标签中......

所以基本上:如何在 JavaScript 中选择多个单词,包括突出显示两个单词之间的所有内容?

编辑有一些我尝试过的代码的请求,所以我试图简化相关部分:

var h= eid("HebrewS");
var currentPhrase=[];
var equal=false;
var shtikles = [

];

h.innerHTML = h.innerHTML.replace(/([\u0590-\u05FF\"\']+)/g,'<span class="shtikle""">$1</span>');

var words = q("#HebrewS span");
words.forEach(function(item, idx){


shtikles[idx] = {obj:item, id:idx, heb:item.innerHTML, translation:"means "+ idx};
item.addEventListener("click", function(e) {


if(currentPhrase.length == 0) {
    currentPhrase.push(idx);
    currentPhrase[1]=idx;
    equal=true;
}
else {
    currentPhrase[1]=idx;

    if(currentPhrase[1] < currentPhrase[0]) {
        currentPhrase.reverse();
    }
    if(currentPhrase[0]==currentPhrase[1]) 
        if(!equal) {
            equal=true;
        } else {
            currentPhrase = new Array();
            equal=false;
        }
    else 
        equal=false;

    }

selectPhrase(currentPhrase);

});

function selectPhrase(p) {

for(var i =0;i<shtikles.length;i++) {
    if(shtikles[i].obj)
    if(p.length > 0) {
    if(i  < p[0] || i > p[1]) {
        if(shtikles[i].obj.className != "shtikle") {

            shtikles[i].obj.className ="shtikle";
        }
    }
    } else {
        shtikles[i].obj.className = "shtikle";
    }
} 
for(var i = p[0]; i <= p[1]; i++) {
    shtikles[i].obj.className="phrasePart";
}
}

function q(a) {
return document.querySelectorAll(a);

}
function eid(id) {
return document.getElementById(id);

}

现在对于html:

<div style="" id ="HebrewS">today I will show,.  you how] to read.. {Maamarim! וחזקת והיית לאיש1, הנה ידוע2 שהמאמר שאמר אדמו"ר (מהורש"ב) נ"ע ביום השביעי3 דחגיגת הבר מצוה של בנו יחידו כ"ק מו"ח אדמו"ר, י"ט תמוז4 תרנ"ג [שמאמר זה הוא סיום וחותם ההמשך תפילין דמארי עלמא5 שהתחיל לומר בי"ב תמוז, יום הבר מצוה] היתה התחלתו בפסוק זה. – השייכות דפסוק זה (וחזקת והיית לאיש) לבר מצוה בפשטות היא, ע"פ הידוע6 דזה שבן שלש עשרה (דוקא) מחוייב במצוות הוא כי אז דוקא נק' בשם איש. וצריך להבין, דמכיון שבן י"ג שנה נעשה איש (ע"פ טבע), מהי ההדגשה לומר (בחגיגת בר מצוה) וחזקת והיית לאיש. וגם צריך להבין, הרי המעלה דבן י"ג שנה היא שאז נעשה בר דעת7, דדעת הוא במוחין, ובפרט לפי המבואר בהמאמר ד"ה איתא במדרש תילים תרנ"ג [שהוא אחד המאמרים שחזר אותם כ"ק מו"ח אדמו"ר בחגיגת הבר  שלו]8 שהמעלה דבן י"ג שנה היא שאז יש לו עצם המוחין9, ומהו הדיוק בבן י"ג שנה בהתואר איש שמורה10 על המדות

现在的CSS:

<style type="text/css">

.shtikle:hover{
    background-color:yellow;
}

.phrasePart{
    background-color: purple;
    border: 0px solid black;
}

</style>

我还没有测试过代码的简化版本,但如果你尝试一下应该可以。

基本点是:它单独选择每个单词,但不会突出显示单词之间的内容(而且我不想将当前短语中的所有单词都放入它们自己的跨度中,因为我想保存短语并稍后选择它,并且还有多个短语,一些单词可能在两者中)

标签: javascripthtmlcss

解决方案


将所有内容拆分为单个节点,然后使用范围。简单的实现可能如下所示。它适用于两次单击(右键单击删除选择)并准备实现单击和滑动(您必须使用一些布尔标志实现 mouseenter 事件侦听器)。所以关键是,在检查每个节点的每次点击 id 之后,它要么成为范围的起点,要么使用 for 循环关闭范围,为中间的每个节点添加类。

然后,您可以将 id 范围存储在某处并在例如激活它们。按钮单击。

//编辑

检查下面的编辑,这完全是一团糟,但我相信它应该符合您的需求。

const txt = 'this is some word in a wordy place where all words are kind of, well... this is some word in a wordy place where all words are kind of something so: this is some word in a wordy place where all words are kind of, and then -> this is some word in a wordy place where all words are kind of nothing';
let startIndex = null;
let lines = document.querySelector('.lines');
lines.innerHTML = txt.split(' ').map((z, i) => {

  return (z.replace(new RegExp("\\w+|\\W+", "g"), (t) => { return /\w+/.test(t) ? `<span data-id="${i}" data-word>${t}</span>` : `<span data-id="${i}">${t}</span>` }));
}).join(' ');
nodes = Array.prototype.slice.call(document.querySelectorAll('.lines span'));
nodes.forEach(z => {
	if(z.hasAttribute('data-word')) {
    z.addEventListener('mousedown', (e) => {
  	const id = Number(e.target.getAttribute('data-id'));
  	if (startIndex === null) {
    	startIndex = id;
    	e.target.classList.add('active');
    } else {
    	const range = id > startIndex ? [startIndex, id] : [id, startIndex];
      for(let i = range[0]; i<= range[1]; i++) {
      	(Array.prototype.slice.call(document.querySelectorAll('span[data-id="' + i + '"]'))).forEach(e => e.classList.add('active'));
      };
      startIndex = null;
    }
  });
  }
});

window.oncontextmenu = function ()
{
    startIndex = null;
    nodes.forEach(z => z.classList.remove('active'))
    return false;
}
.lines {
  user-select: none;
}
.lines span {
  display: inline-block;
  padding:3px;
  box-decoration-break: clone;
  transition:.2s;
}
.lines span.active { 
  background: salmon;
  box-shadow: 3px 0 0 salmon, -3px 0 0 salmon;
}
[data-word] {
  cursor:pointer;
}
<div class="lines"></div>


推荐阅读