批注,指阅读时在文中空白处对文章进行批评和注解,作用是帮助自己掌握书中的内容。批注是我国文学鉴赏和批评的重要形式和传统的读书方法,它直入文本、少有迂回,多是些切中肯綮的短词断句,是阅读者自身感受的笔录,体现着阅读者别样的眼光和情怀。
最近因项目需要,要给html页面文章内容添加批注,完成批改作业的需求。查询资料,相关实例很好,有的也存在很多兼容性问题。
鉴于手机页面没法选择文字操作(andriod和ios处理不同,系统原因),这里只介绍pc的端批注的实现原理:
1、选择要添加批注的文字(这里存在一个问题,选择文字不允许跨段落,会破坏段落结构);
2、定位,添加标签标记及批注内容;
3、批注的保存及初始化;
4、批注的查看;
下来具体看下代码的实现:
html内容区:
<div class="ibox-content ho"> <div class="clearfix"> <!--批注按钮-默认隐藏--> <div class="annotation-icon"> <img src="../images/pop_btn_line.png" class="pull-left j-line-btn"> <img src="../images/pop_btn_add.png" class="pull-right j-an-btn"> </div> <!--文章内容区--> <div class="article-box"> <div class="title">春天里</div> <div class="name-info"> <span>作者:余小璐</span> <span>江苏省南京市西华门小学三年级2班</span> </div> <div class="cc article-box-cc"> <p>《活着》讲述一个人一生的故事,这是一个历尽世间沧桑和磨难老人的人生感言,是一幕演绎人生苦难经历的戏剧。小说的叙述者“我”在年轻时获得了一个游手好闲的职业——去乡间收集民间歌谣。在夏天刚刚来到的季节,遇到那位名叫福贵的老人,听他讲述了自己坎坷的人生经历: 地主少爷福贵嗜赌成性,终于赌光了家业一贫如洗,穷困之中福贵因母亲生病前去求医,没想到半路上被国民党部队抓了壮丁,后被解放军所俘虏,回到家乡他才知道母亲已经过世,妻子家珍含辛茹苦带大了一双儿女,但女儿不幸变成了哑巴。讲述一个人一生的故事,这是一个历尽世间沧桑和磨难老人的人生感言,是一幕演绎人生苦难经历的戏剧。小说的叙述者“我”在年轻时获得了一个游手好闲的职业——去乡间收集民间歌谣。在夏天刚刚来到的季节,遇到那位名叫福贵的老人,听他讲述了自己坎坷的人生经历: 地主少爷福贵嗜赌成性,终于赌光了家业一贫如洗,穷困之中福贵因母亲生病前去求医,没想到半路上被国民党部队抓了壮丁,后被解放军所俘虏,回到家乡他才知道母亲已经过世,妻子家珍含辛茹苦带大了一双儿女,但女儿不幸变成了哑巴。讲述一个人一生的故事,这是一个历尽世间沧桑和磨难老人的人生感言,是一幕演绎人生苦难经历的戏剧。小说的叙述者“我”在年轻时获得了一个游手好闲的职业——去乡间收集民间歌谣。在夏天刚刚来到的季节,遇到那位名叫福贵的老人,听他讲述了自己坎坷的人生经历: 地主少爷福贵嗜赌成性,终于赌光了家业一贫如洗,穷困之中福贵因母亲生病前去求医,没想到半路上被国民党部队抓了壮丁,后被解放军所俘虏,回到家乡他才知道母亲已经过世,妻子家珍含辛茹苦带大了一双儿女,但女儿不幸变成了哑巴。</p> </div> </div> </div> </div>
js实现(用到了layer的弹窗):
/** * @Desc: 添加划线、批注js * @Author: yuxang * @Mailto: 365879415@qq.com * @Date: 2017-02-10 */ 'use strict'; define(function (require, exports, module) { var layer = layui.layer, form = layui.form(), element = layui.element(); module.exports = { init: function () { var _me = this; _me.loader(); _me.selectText(); _me.line_func(); _me.annotation_func(); }, // 选取文字操作 selectText: function () { var _me = this; $(".article-box-cc").mouseup(function (e) { var selectedText; if (window.getSelection) { selectedText = window.getSelection().toString(); } else if (document.selection && document.selection.createRange) { selectedText = document.selection.createRange().text; } if (selectedText) { var xy = _me.getMousePos(e); $(".annotation-icon").css({ "left": xy.x - 90, "top": xy.y - 160 }).fadeIn(300); } else { $(".annotation-icon").hide(); } }); }, // 划线操作 line_func: function () { var _me = this; $(".annotation-icon").find('.j-line-btn').click(function () { $(".annotation-icon").hide(); _me.addLine(); }); }, //批注操作 annotation_func: function () { var _me = this; $(".annotation-icon").find('.j-an-btn').click(function () { $(".annotation-icon").hide(); _me.addPostil(); }); }, // 判断浏览器 isRange: function () { var range = {}; if (window.getSelection) { range.other_range = window.getSelection().getRangeAt(0); } else if (document.selection && document.selection.createRange) { range.ie_range = document.selection.createRange(); } return range; }, // 获取当前鼠标位置 getMousePos: function (event) { var e = event || window.event; var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; var scrollY = document.documentElement.scrollTop || document.body.scrollTop; var x = e.pageX || e.clientX + scrollX; var y = e.pageY || e.clientY + scrollY; //alert('x: ' + x + '\ny: ' + y); return {'x': x, 'y': y}; }, // 标记内容 markContent: function (a, b, v, t) { if (a) { /* //IE之外的浏览器,如果在选择内容包含其他标签的一部分的时候会报异常 var mark = document.createElement("ins"); mark.setAttribute("comment", value); mark.className = "postil"; mark.id=new Date().getTime(); other_range.surroundContents(mark); */ var s = a.extractContents().textContent; var text = "[ins id='" + (new Date().getTime()) + "' comment='" + v + "']" + s + "[/ins]"; var textNode = document.createTextNode(text); a.insertNode(textNode); var content = $(".article-box-cc").html(); var reg = /\[ins id='(\d*)' comment='([\w\W]*)']([\w\W]*)\[\/ins]/gi; reg.test(content); var id = RegExp.$1, comment = RegExp.$2, c = RegExp.$3; var reHtml = "<ins id='" + id + "' comment='" + comment + "' class='postil' >" + c + "</ins>" + t; content = content.replace(reg, reHtml); $(".article-box-cc").html(content); } else if (b) { b.pasteHTML("<ins comment='" + v + "' class='postil' id='" + new Date().getTime() + "'>" + b.htmlText + "</ins>" + t); b = null; } }, // 添加划线 addLine: function () { var _me = this, r = _me.isRange(), ie = r.ie_range, //IE支持的range对象 o = r.other_range, //其他浏览器的range对象 v = "", atip = ''; _me.markContent(o, ie, v, atip); _me.loader(); }, // 添加批注 addPostil: function () { var _me = this, r = _me.isRange(), ie = r.ie_range, //IE支持的range对象 o = r.other_range, //其他浏览器的range对象 html = '<div class="clearfix" style="margin: 20px;">\ <form class="layui-form" action="">\ <textarea name="review" required lay-verify="required" placeholder="请输入批注..." class="layui-textarea" autofocus></textarea>\ <div class="review-btn-grp">\ <button class="layui-btn layui-btn-red pull-right j-sure" lay-submit="" lay-filter="demo">确定</button>\ <span class="layui-btn layui-btn-primary pull-left j-cancel">取消</span>\ </div>\ </form>\ </div>', i = layer.open({ type: 1, title: false, //不显示标题 closeBtn: 0, //不显示关闭按钮 anim: 2, area: ['360px', 'auto'], //宽高 content: html }); /*取消*/ $('.j-cancel').on('click', function () { layer.close(i); }); /*监测提交*/ form.on('submit(demo)', function (data) { var v = data.field.review, atip = "<span class='annotation-tip'>我的批注</span>"; _me.markContent(o, ie, v, atip); _me.loader(); layer.close(i); return false;//阻断form的提交页面刷新 }); }, // 解析批注 loader: function () { var _me = this; layer.closeAll(); $.each($(".article-box-cc ins"), function (a, b) { var id = $(b).get(0).id, t, c = $(b).attr("comment"); if (!c) { return; } $(b).hover(function () { $(this).next('.annotation-tip').attr('myid', id) .show() .unbind('click') .on('click', function () { var _id = $(this).attr('myid'), _comment = $('#' + _id).attr('comment'); var postil = "<div class='text-right' style='margin: 16px'>\ <p class='annotation-box2' forid='" + _id + "'>" + _comment + "</p>\ <button class='layui-btn layui-btn-mini text-right layui-btn-red j-del-annotation'>删除批注</button>\ </div>"; layer.open({ type: 1, title: 0, // skin: 'layui-layer-red', area: '400px', //宽高 anim: 2, content: postil }); //删除批注 $('.j-del-annotation').unbind('click').on('click', function () { _me.removePostil($(this).parent().find('p')); }); }); clearTimeout(t); }, function () { t = setTimeout(function () { $('.annotation-tip').hide(); }, 1000); }); }); }, // 删除批注 removePostil: function (arg) { var _me = this; var id = arg.attr("forid"); var $source = $("#" + id); var text = $source.after($source.html()); $source.next('span.annotation-tip').remove(); $source.remove(); _me.loader(); }, // 循环减 loopMinus: function (n1, n2) { var _me = this; if (n1 > n2) { n1 -= n2; _me.loopMinus(n1, n2); } }, // 计算字符串实际像素长度 calcStringPixelsCount: function (str, strFontSize) { // 字符串字符个数 var stringCharsCount = str.length; // 字符串像素个数 var stringPixelsCount = 0; // JS 创建HTML元素:span var elementPixelsLengthRuler = document.createElement("span"); elementPixelsLengthRuler.style.fontSize = strFontSize; // 设置span的fontsize elementPixelsLengthRuler.style.visibility = "hidden"; // 设置span不可见 elementPixelsLengthRuler.style.display = "inline-block"; elementPixelsLengthRuler.style.wordBreak = "break-all !important"; // 打断单词 // 添加span document.body.appendChild(elementPixelsLengthRuler); for (var i = 0; i < stringCharsCount; i++) { // 判断字符是否为空格,如果是用 替代,原因如下: // 1)span计算单个空格字符( ),其像素长度为0 // 2)空格字符在字符串的开头或者结果,计算时会忽略字符串 if (str[i] == " ") { elementPixelsLengthRuler.innerHTML = " "; } else { elementPixelsLengthRuler.innerHTML = str[i]; } stringPixelsCount += elementPixelsLengthRuler.offsetWidth; } return stringPixelsCount; } }; });
未经允许不得转载:技术人生 » 给html页面文章添加批注或划线
除了参考示例,有没有demo
http://velo-sport.sumy.ua/?p=history&hp=2006