欢迎光临
技术人生-雨巷前端

给html页面文章添加批注或划线

批注,指阅读时在文中空白处对文章进行批评和注解,作用是帮助自己掌握书中的内容。批注是我国文学鉴赏和批评的重要形式和传统的读书方法,它直入文本、少有迂回,多是些切中肯綮的短词断句,是阅读者自身感受的笔录,体现着阅读者别样的眼光和情怀。

最近因项目需要,要给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++) {
                // 判断字符是否为空格,如果是用&nbsp;替代,原因如下:
                // 1)span计算单个空格字符( ),其像素长度为0
                // 2)空格字符在字符串的开头或者结果,计算时会忽略字符串
                if (str[i] == " ") {
                    elementPixelsLengthRuler.innerHTML = "&nbsp;";
                } else {
                    elementPixelsLengthRuler.innerHTML = str[i];
                }
                stringPixelsCount += elementPixelsLengthRuler.offsetWidth;
            }
            return stringPixelsCount;
        }
    };
});

参考实例

 

 

未经允许不得转载:技术人生 » 给html页面文章添加批注或划线

分享到:更多 ()

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #2

    除了参考示例,有没有demo

    长弓飞羽羊11个月前 (09-06)回复
  2. #1

我是前端,我不迷茫