前几天我遇到了一个独特的编程挑战,我想在这里分享一下。它非常具体,可能不会有广泛的通用性,但嘿,它可能很有趣。
想法是当您点击文本块中的任何字母时,让 160 个字符变为“选中”状态。
我认为不可能像那样自动“真正”选择文本,因为这对我来说有点像操作系统级功能。不过我真的不知道,如果我错了,请纠正我。所以,我决定做的是“伪造”它,通过在适当的字符后面应用背景颜色。在我们正在做的事情的背景下,这仍然有效。然后,相同的字符被移到文本框中,以便进行潜在的编辑和提交。
对我来说,最困难的部分是想到一种方法来找出文本串中的哪个**字符**被点击了。所以,我再次作弊了。我想,如果我将每个字符都用<span>包裹起来,我就可以监控每个跨度的点击事件。同样好。jQuery 一如既往
var theText = $("#theText");
var theString = theText.text();
var numCharacters = theString.length;
var newHTML = "";
for (i = 0; i <= numCharacters; i++) {
var newHTML = newHTML + "" + theString[i] + "";
}
theText.html(newHTML);
现在,我将点击事件绑定(绑定?)到每个新的用跨度包裹的字符上。当它们被点击时,会从所有字符中移除“选中”类,并将其应用到被点击的字符上。然后,一个 for 循环启动,循环 160 次。它移到下一个字符并突出显示它(通过添加一个类)。
$("span").click(function(){
$("span").removeClass("selected");
$(this).addClass("selected");
var nextSpan = $(this);
for (i = 1; i <= 160; i++) {
nextSpan = nextSpan.next();
nextSpan.addClass("selected");
}
});
因为我还想将这个新选择的文本移到一个文本框中(以便进行进一步的编辑),我在点击函数中运行了第二个循环。这个第二个循环遍历当前选中的每个字符,并将它附加到 jQuery 数据块中保存的字符串中。在循环结束时,该数据块被应用到文本区域。
$("#result").data("result", "");
$(".selected").each(function() {
var oldResults = $("#result").data("result");
var newResults = oldResults + $(this).text();
$("#result").data("result", newResults);
});
$("#result").val($("#result").data("result"));
因为这个目标是最终将字符提交到另一个 URL,所以我创建了一个按钮来执行此操作。当该按钮被点击时,它获取文本区域的值,将其附加到一个 URL,然后将其发送出去。
$("#sendit").click(function() {
var toURL = "?=" + $("#result").val();
window.location = toURL;
return false;
});
所以,再次强调,非常具体,我确信它没有广泛的用处,但我从未见过任何其他类似的功能,所以也许它对处于类似位置的人会有用。
更新:正如我所希望的,有人提出了另一种(更智能的)方法:Matt Wondra 这里 提供了一个演示。它利用了文本区域和 JavaScript 的“范围”。唯一的缺点是文本区域固有的愚蠢之处,即无法随着内容的高度增长而增长。
非常棒的小技巧。我喜欢。我跟你说吧,伙计。jquery-tricks.com。未来。=)
这对一些未来的项目会有帮助,.. 谢谢。
当我看到标题时,我的第一个想法是“为什么是 160 个字符?Twitter 限制为 140 个”。虽然你的帖子与 Twitter 无关,但它提供了一个想法。
我刚刚创建了一个 Firefox 插件(ReplacURLr),它将位置栏中的文本替换为一个缩短的 URL,用于通过 TwitterBar 发布推文。我认为我可能会添加一个选项,将 140 个字符的文本复制到栏中。感谢你的灵感!
你说得对,Twitter 的字符限制是 140 个,但短信通常是 160 个,Twitter 允许只有 140 个,这样他们就可以添加一个姓名。所以实际上就像 160 个。
嗨,Chris。
我不确定这对您来说是否重要,但短信并不总是 160 个字符。它是 160 个标准字符,但很多纯 ASCII 字符在 GSM 字符集中被表示为双字符 - 您可能经常使用的一些字符,例如 €、[、]、{、}、^。
这里有一个完整列表:http://www.mxtelecom.com/us/tech/knowledge_base.jsp?faqid=5#question7.
如果这对您很重要,我可以帮助您,请给我留言。
Tom
如果你在批准按钮附近放一个复制到剪贴板按钮,那就更好了。
我的第一个本能是,你应该能够让 JavaScript 计算出文本块的长度,并帮助你完成这项工作,而无需将每个字符都包裹在跨度中 - 但现在我仔细想想,我认为这不可能(JavaScript 如何将光标位置与字符编号相关联?有人知道吗?)。
此外,你必须运行一些非常高级的东西才能使选定块的“部分”突出显示。尽管我想说还有更好的方法,但我认为你已经找到了问题的症结所在。
现在你让我很好奇,Chris。我知道有一种更简单的方法,但至少你的方法是有效的 :)
我知道肯定有更好的方法。在这里运行两个相当慢的循环。但是的,它确实有效。
这听起来像一个非常有趣且具有挑战性的问题。你的解决方案非常棒,但就像 Eric 一样,我渴望看看是否还有更好的方法…
让我感到不安的最大问题之一是这会破坏“复制”功能 - 我内心深处的可用性纯粹主义者说,如果看起来文本被选中了,ctrl/cmd+c 应该有效。
我想知道(只是凭空想) - 你是否考虑过将文本放在文本区域元素中?我确信有一些 JavaScript 功能可以处理当前光标位置等,你可以始终将它样式化为不看起来像文本区域。当然,如果你是一个真正的纯粹主义者,你将与这种非语义性的做法作斗争,但我不知道字符跨度是否更好…
无论如何,只是好奇。如果你还没有尝试过(或者已经排除了它不可行),我今天晚些时候可能会尝试一下。
一如既往,很棒的工作!
确实是一个不错的技巧。
但是,它无法通过“正常”方式选择文本:点击和拖动,我认为大多数非技术用户会以这种方式选择文本。
尽管如此,这仍然是一个很酷的技巧。
谢谢。
我想知道 jQuery wrap 函数是否有一个地方可以自动包裹字符,但我觉得解决方案可能在于许多编码网站上的 JS 框,这些框具有“全选”选项,类似于 snipplr.com。
也许我们需要开始将文本放在“textarea”而不是“p”中。
只是一个想法。
您可以使用 JavaScript 的 Selection API 获取选定范围,扩展范围等等。以下是对 Mozilla 的说明。
MDC Selection
我从未真正使用过范围,但应该可以通过使用范围并将它们添加到选择来实现这一点。
https://mdn.org.cn/en/DOM/Selection
https://mdn.org.cn/en/DOM/range
http://christophdum.com/syntaxhighligher/grabber019.htm
(点击文本并查看光标 + 选择文本 -> 查看发生了什么(它有点有效,尽管它很 buggy(例如:只选择段落内的文本,因为它会移除选择中的所有 HTML)))。
我已经尝试了一段时间使用 JS 文本选择(我最终想创建一个文本区域替换(也许有一天还会创建一个漂亮的 JS 所见即所得编辑器或语法高亮器))。
无论如何,包裹每个字符是我的第一个想法,但这很糟糕。
使用范围,不会添加任何不必要的 HTML,并且它会将光标精确地放置在您想要的位置(如果您点击字符左侧,光标将被放置在左侧 / 如果您点击右侧,光标将被插入到字符之后(就像在真正的文本编辑器中一样))。
PS:对于所有关心可用性的朋友:如果选择了文本,您可以将该文本复制到对用户不可见的文本框中,并对其进行 .focus(),这样用户就可以复制它。
PPS:这只是我的一个有趣项目 - 代码很丑陋,而且有很多 bug - 只是一个概念证明(我通常会编写好的代码并使用 jQuery ;) )。
@Sean + @ddunlop – 问题更多在于找到用户点击的精确字符。如果你已经知道想要哪个范围,使用 Selection 和 range 就很容易。这就是为什么 Chris 必须将每个字符放在一个 span 中 - 这样 javascript 就会知道点击了哪个字母。
另外一个消息——我用 textarea 做到了!:D
查看 http://mattwondradesign.com/tests/highlightchars/ 的演示。
使用 textareas 最大的不幸是它们在垂直方向上不灵活,所以你需要添加另一个 javascript 钩子来调整 textarea 的大小以适应内容。但它允许 ctrl/cmd-c 并且不需要 spans 来找到点击!
不确定是否可以称之为改进,但也许只是另一种方法。
不错!绝对是改进。textareas 无法动态调整大小真是个遗憾。这在 HTML-Ipsum 上有点问题:http://html-ipsum.com/
我会更新文章,也指向你的更好解决方案。
这是可能的:http://james.padolsey.com/javascript/jquery-plugin-autoresize/
太好了。已经实现。
http://html-ipsum.com
当你点击一个字符时,窗口选择的锚点被设置为你点击的字符。所以这个简单的函数(+ mootools addEvent)会选择接下来的 140 个字符或节点的剩余部分。
window.addEvent('domready',function() {
$$('p').addEvent('click',function(e) {
var s = window.getSelection();
s.extend(s.anchorNode, s.anchorOffset + 140 < s.anchorNode.length ? s.anchorOffset + 140 : s.anchorNode.length);
});
});
只需要再多做一点工作,你就可以存储额外的长度,然后从下一个节点中添加那么多字符到选择中。
Selection API 确实让这样的任务变得超级简单 :)
如果这在任何地方都能奏效就好了……据我所知,除非你实际上选择了一些文本(例如,如果你只是点击),否则在 Opera 中,anchorNode 只是你点击的元素的整个父节点,而 anchorOffset 始终为 0
$('p').click(function(e) { // jQuery
s = window.getSelection();
alert(s.anchorOffset);
});
当你考虑它的时候,这是公平的:点击不会在任何真正意义上进行选择。我还没有在 IE 中测试过,但我没有抱太大希望……
这让我们又回到了需要再次为每个字符设置 span 的问题……
好技巧!
半脱题问题:你网站中选定的文本如何变成橙色?jQuery?CSS3?
无论如何,谢谢 :)
我认为使用 range 技巧更好,但 textarea 还有另一个问题,
如果文本不是纯文本,而是 html,例如网页内容,我认为 range 也可能存在另一种解决方案。
在我读这篇文章之前,我在 stackoverflow 上问过这个问题,关于多背景色的文本框:http://stackoverflow.com/questions/700433/ideas-for-multicolored-textbox。
你的技术(以及由此产生的评论)给了我一些灵感。
@cant
我也对选定文本技巧印象深刻。答案在之前的帖子中
https://css-tricks.org.cn/overriding-the-default-text-selection-color-with-css/
感谢 Chris,干得漂亮!