blog项目 评论楼【评论树升级版】
url路由配置:
url(r'^comment',views.comment),
views视图函数:

1 def comment(request): 2 content = request.POST.get('content') 3 article_id = request.POST.get('article_id') 4 user_id = request.user.pk 5 pidd = request.POST.get('pd') 6 if pidd: # 子评论 7 comment = Comment.objects.create(content=content, article_id=article_id, user_id=user_id, parent_comment_id=pidd) 8 else: # 根评论 9 comment = Comment.objects.create(content=content, article_id=article_id, user_id=user_id) 10 11 return HttpResponse("ok")
前端模板:

1 {% extends 'step1/site_base.html' %} 2 {% block content %} 3 <div class="article_info"> 4 <h4 class="text-center"><a href="">{{ article_obj.title }}</a></h4> 5 {{ article_obj.articledetail.content|safe }} 6 </div> 7 <div class="clearfix"> 8 <div id="green_channel"> 9 <a href="javascript:void(0);" id="green_channel_digg" 10 onclick="DiggIt(8343243,cb_blogId,1);green_channel_success(this,'谢谢推荐!');">好文要顶</a> 11 <a id="green_channel_follow" onclick="follow('ae376d01-5dac-e511-9fc1-ac853d9f53cc');" 12 href="javascript:void(0);">关注我</a> 13 <a id="green_channel_favorite" onclick="AddToWz(cb_entryId);return false;" 14 href="javascript:void(0);">收藏该文</a> 15 <a id="green_channel_weibo" href="javascript:void(0);" title="分享至新浪微博" onclick="ShareToTsina()"><img 16 src="/static/img/icon_weibo_24.png" alt=""></a> 17 <a id="green_channel_wechat" href="javascript:void(0);" title="分享至微信" onclick="shareOnWechat()"><img 18 src="/static/img/wechat.png" alt=""></a> 19 </div> 20 21 <div id="div_digg"> 22 <div class="diggit digg"> 23 <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span> 24 </div> 25 <div class="buryit digg"> 26 <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span> 27 </div> 28 <div class="clear"></div> 29 <div class="diggword" id="digg_tips"></div> 30 </div> 31 </div> 32 33 <div class="comment_show"> 34 <p>评论楼: </p> 35 <ul class="comment_list list-group"> 36 {# 这里的comment_list没错,是要从后端把数据取出来然后放到这里来进行渲染,但是我们的这个前端模板是article_detail,#} 37 {#它有自己专属的视图函数来渲染它,所以我们要取数据渲染它就需要去它的函数里面渲染,也就是article_detail函数里面#} 38 {% for comment in comment_list %} 39 <li class="comment_item list-group-item"> 40 <div class="row"> 41 <div class="col-md-offset-1"> 42 <a href="">#{{ forloop.counter }}楼</a> 43 <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> 44 <span>{{ comment.user.username }}</span> 45 <div class="pull-right"> 46 <a class="reply_btn" href="#" comment_id="{{ comment.pk }}" 47 comment_user="{{ comment.user.username }}">回复</a> 48 </div> 49 </div> 50 </div> 51 52 <div class="row"> 53 <div class="col-md-offset-1"> 54 <p>{{ comment.content }}</p> 55 </div> 56 </div> 57 </li> 58 {% endfor %} 59 60 </ul> 61 </div> 62 63 <div class="comment"> 64 <p> 65 <label for="tbCommentAuthor">昵称: </label> 66 <input type="text" id="tbCommentAuthor" class="author" disabled size="50" 67 value="{{ request.user.username }}"> 68 </p> 69 70 <div> 71 <p><label for="">评论内容:</label></p> 72 <p><textarea type="text" id="comment_area" cols="60" rows="5"></textarea></p> 73 <input type="button" id="comment_submit_btn" class="btn btn-primary" value="提交"> 74 <input type="button" id="comment_cancel" class="btn btn-primary" value="取消"> 75 </div> 76 </div> 77 {% csrf_token %} 78 79 <script> 80 {# $(".digg").click(function(){#} 81 {# alert('hello')#} 82 {# });#} 83 //点赞按钮 84 $(".digg").click(function () { 85 if ("{{ request.user.username }}") { 86 var is_up = $(this).hasClass("diggit");//我们在这里定义了如果div里面有这个class属性的话它就是true否则就是false,因为我们的is_up是bool类型 87 {# console.log(is_up);#} 88 $.ajax({ 89 url: "/step/dig/", 90 type: "post", 91 data: { 92 article_id:{{ article_obj.pk }}, 93 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), 94 is_up: is_up 95 }, 96 success: function (data) { 97 var error_info; //声明一个变量共后续使用 98 if (data.state) { //如果点赞操作成功的话就走这一步 99 if (is_up) { 100 var vae=parseInt($("#digg_count").text())+1; 101 $("#digg_count").text(vae); 102 } 103 else { 104 var val=parseInt($("#bury_count").text())+1; 105 $("#bury_count").text(val); 106 } 107 } 108 else { //如果点赞操作失败的话就走这一步 109 if (data.first_updown) { 110 error_info="已经点赞过了" 111 } else { 112 error_info="已经踩灭过了" 113 } 114 115 $("#digg_tips").html(error_info).css("color", "red"); 116 117 {# setTimeout(function () {#} 118 {# $("#digg_tips").html("")#} 119 {# }, 5000)#} 120 } 121 } 122 }) 123 } 124 else { 125 alert(123) 126 } 127 }); 128 129 130 //基于ajax绑定提交评论事件 131 var parent_comment_pk = ""; 132 $("#comment_submit_btn").click(function () { 133 //区分跟评论和子评论基于一个变量parent_comment_pk,我们的评论是分为根评论和子评论的,要区分对待才能顺利提交, 134 // 因为二者是共享的一个事件为了节省代码,所以就在这里把他们区分开进行处理 135 var content = $("#comment_area").val(); //我们在前端模板语言里面只有三个标签是可以调用val()方法的, 136 // 第一个就是input,第二个就是select,第三个是textarea 请务必牢记 137 if (parent_comment_pk) { //子评论如果if判断结果为true就是子评论,否则为根评论 138 var index = content.indexOf("\n"); 139 content = content.slice(index + 1); 140 } else { //根评论 141 var content = $("#comment_area").val(); 142 } 143 144 $.ajax({ //我们的ajax是在click里面的,我们要点击然后才能把数据提交到后端进行处理 145 url: "/step/comment/", 146 type: "post", 147 data: { 148 content: content, 149 article_id: "{{ article_obj.pk }}", 150 csrfmiddlewaretoken: $("[name=csrfmiddlewaretoken]").val(), 151 pd: parent_comment_pk 152 }, 153 success: function (data) { 154 console.log(data); 155 } 156 }) 157 }); 158 159 160 //绑定回复按钮事件 这样写出来是可以在数据库看到他,但是我们在浏览器级别是看不到它的,需要让它在浏览器级别就显示出来该回复是回复的谁,一目了然 161 $(".comment_item .reply_btn").click(function(){ 162 //获取焦点 163 $("#comment_area").focus(); 164 //设置:@用户名 165 var vae="@"+$(this).attr("comment_user")+"\n"; 166 $("#comment_area").val(vae); 167 //获取回复评论的主键值 parent_comment_pk 168 parent_comment_pk=$(this).attr("comment_id") 169 }); 170 171 //取消评论内容 172 $("#comment_cancel").click(function(){ 173 $("textarea").val(""); 174 }); 175 176 </script> 177 178 {% endblock %}
在前端模板里面单独摘出来的js代码:

//基于ajax绑定提交评论事件 var parent_comment_pk = ""; $("#comment_submit_btn").click(function () { //区分跟评论和子评论基于一个变量parent_comment_pk,我们的评论是分为根评论和子评论的,要区分对待才能顺利提交, // 因为二者是共享的一个事件为了节省代码,所以就在这里把他们区分开进行处理 var content = $("#comment_area").val(); //我们在前端模板语言里面只有三个标签是可以调用val()方法的, // 第一个就是input,第二个就是select,第三个是textarea 请务必牢记 if (parent_comment_pk) { //子评论如果if判断结果为true就是子评论,否则为根评论 var index = content.indexOf("\n"); content = content.slice(index + 1); } else { //根评论 var content = $("#comment_area").val(); } $.ajax({ //我们的ajax是在click里面的,我们要点击然后才能把数据提交到后端进行处理 url: "/step/comment/", type: "post", data: { content: content, article_id: "{{ article_obj.pk }}", csrfmiddlewaretoken: $("[name=csrfmiddlewaretoken]").val(), pd: parent_comment_pk }, success: function (data) { console.log(data); } }) }); //绑定回复按钮事件 这样写出来是可以在数据库看到他,但是我们在浏览器级别是看不到它的,需要让它在浏览器级别就显示出来该回复是回复的谁,一目了然 $(".comment_item .reply_btn").click(function(){ //获取焦点 $("#comment_area").focus(); //设置:@用户名 var vae="@"+$(this).attr("comment_user")+"\n"; $("#comment_area").val(vae); //获取回复评论的主键值 parent_comment_pk parent_comment_pk=$(this).attr("comment_id") }); //取消评论内容 $("#comment_cancel").click(function(){ $("textarea").val(""); });
相应的配置的静态文件:

1 input.author{ 2 background-position:3px -3px; 3 } 4 5 input.author{ 6 background-image:url("/static/img/icon_form.gif"); 7 background-repeat:no-repeat; 8 border:1px solid #ccc; 9 padding:4px 4px 4px 30px; 10 width:300px; 11 font-size:13px; 12 } 13 14 .reply_btn{ 15 margin-right:10px; 16 }
=======================================================================================================================================
我们的评论功能一般都是使用评论楼,包括我们所熟知的各大论坛以及各种社交网站,因为评论楼更加便于管理,实现起来也简单得多。但是我们还是需要多学习一下其他的方式
比如我们的评论树功能,【评论树和评论楼最根本的区别就是评论楼可以限制楼层,我最多可以显示多少层,让回复的层级显示出来,再多的楼层,我就不以楼层的形式显示出来了,也就是说对于评论的评论我们的显示不是无限制的,比如微博的骂架,我们如果使用评论楼的话就没有办法把完整的骂战呈现出来,会有断片,所以这个时候评论树就凸现出来了,微博它本身就是依赖于用于的粘性而发展至今的,所以他的评论是核心功能。这个是根据产品而定的,很多时候并不需要那么多的评论树去展开,一般的评论楼层已经足够了】
评论树就是我们把数据罗列出来,然后根据该评论的父id去进行筛选,只要此评论的父id跟彼评论的id一致那么此评论就放到彼评论的子评论列表里面,所以我们需要在后端先把这样的数据结构生成,要把子评论列表建立起来,
我们的评论树的数据构建:

1 评论树的展开逻辑流程,我们通过 2 comment_list=[ 3 {"id":1,"content":"111","Pid":None},{"id":2,"content":"222","Pid":None},{"id":3,"content":"333","Pid":None}, 4 {"id":4,"content":"444","Pid":1},{"id":5,"content":"555","Pid":1},{"id":6,"content":"666","Pid":4}, 5 {"id":7,"content":"777","Pid":3},{"id":8,"content":"888","Pid":7},{"id":9,"content":"999","Pid":None}, 6 ] 7 8 for obj in comment_list: 9 obj["children_list"]=[] 10 11 ret = [] 12 for obj in comment_list: 13 pid = obj['Pid'] 14 if pid: 15 for i in comment_list: 16 if i['id'] == pid: # 这里是判断该字典是否有父评论 17 i['children_list'].append(obj) 18 else: 19 ret.append(obj) 20 print(ret)
先来路由系统:
url(r'^show_tree/$'),views.show_tree),
后端数据处理views视图:

1 # 文章详细页 2 def article_detail(request, username, article_id): 3 article_obj = Article.objects.filter(pk=article_id).first() 4 comment_list = Comment.objects.filter(article_id=article_id) 5 # count = article_obj.comment_count + 1 # 这里本来是要把评论数传到前端使用的,但是我们在前端的ajax里面已经实现了就不用麻烦后端去处理了 6 comment_li = Comment.objects.filter(article_id=article_id).\ 7 values("nid", "content", 'create_time', 'parent_comment_id', 'user__username') 8 for obj in comment_li: 9 obj["children_list"] = [] 10 11 ret = [] 12 for obj in comment_li: 13 pid = obj['parent_comment_id'] 14 if pid: 15 for i in comment_li: 16 if i['nid'] == pid: # 这里是判断该字典是否有父评论 17 i['children_list'].append(obj) 18 else: 19 ret.append(obj) 20 # print(ret) # 这里我们已经把它获取到了预期的结构,然后就是把它渲染到页面上了,该怎么做?已经解决了,在前端的js里面实现的 21 return render(request, 'step1/article_detail.html', 22 {'username': username, 'article_obj': article_obj, 23 'comment_list': comment_list}, )

1 def get_tree(request, article_id): 2 comment_list = list(Comment.objects.filter(article_id=article_id).values("nid", "content", 'create_time', "parent_comment_id", "user__username").all()) 3 for i in comment_list: 4 i['create_time'] = i['create_time'].strftime('%Y-%m-%d %H:%M:%S') 5 # print(comment_list) 6 from django.http import JsonResponse 7 return JsonResponse(comment_list, safe=False)
如下就是我们的前端HTML模板:

1 {% extends 'step1/site_base.html' %} 2 {% block content %} 3 <div class="article_info"> 4 <h4 class="text-center"><a href="">{{ article_obj.title }}</a></h4> 5 {{ article_obj.articledetail.content|safe }} 6 </div> 7 <div class="clearfix"> 8 <div id="green_channel"> 9 <a href="javascript:void(0);" id="green_channel_digg" 10 onclick="DiggIt(8343243,cb_blogId,1);green_channel_success(this,'谢谢推荐!');">好文要顶</a> 11 <a id="green_channel_follow" onclick="follow('ae376d01-5dac-e511-9fc1-ac853d9f53cc');" 12 href="javascript:void(0);">关注我</a> 13 <a id="green_channel_favorite" onclick="AddToWz(cb_entryId);return false;" 14 href="javascript:void(0);">收藏该文</a> 15 <a id="green_channel_weibo" href="javascript:void(0);" title="分享至新浪微博" onclick="ShareToTsina()"><img 16 src="/static/img/icon_weibo_24.png" alt=""></a> 17 <a id="green_channel_wechat" href="javascript:void(0);" title="分享至微信" onclick="shareOnWechat()"><img 18 src="/static/img/wechat.png" alt=""></a> 19 </div> 20 21 <div id="div_digg"> 22 <div class="diggit digg"> 23 <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span> 24 </div> 25 <div class="buryit digg"> 26 <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span> 27 </div> 28 <div class="clear"></div> 29 <div class="diggword" id="digg_tips"></div> 30 </div> 31 </div> 32 33 {#评论树#} 34 <p class="tree_click">评论树: </p> 35 36 <div class="comment_tree"> 37 </div> 38 39 40 {#评论楼#} 41 <div class="comment_show"> 42 <p>评论楼: </p> 43 <ul class="comment_list list-group"> 44 {# 这里的comment_list没错,是要从后端把数据取出来然后放到这里来进行渲染,但是我们的这个前端模板是article_detail,#} 45 {#它有自己专属的视图函数来渲染它,所以我们要取数据渲染它就需要去它的函数里面渲染,也就是article_detail函数里面#} 46 {% for comment in comment_list %} 47 <li class="comment_item list-group-item"> 48 <div class="row"> 49 <div class="col-md-offset-1"> 50 <a href="">#{{ forloop.counter }}楼</a> 51 <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> 52 <span>{{ comment.user.username }}</span> 53 <div class="pull-right"> 54 <a class="reply_btn" href="#" comment_id="{{ comment.pk }}" 55 comment_user="{{ comment.user.username }}">回复</a> 56 </div> 57 </div> 58 </div> 59 60 {% if comment.parent_comment_id %} 61 <div class="row"> 62 <div class="col-md-offset-2 well"> 63 <a href="">@ {{ comment.parent_comment.user.username }}</a> 64 <span>{{ comment.parent_comment.content }}</span> 65 <div class="pull-right"> 66 <a href="#" class="reply_btn" comment_id="{{ comment.pk }}" 67 comment_user="{{ comment.user.username }}">回复</a> 68 </div> 69 </div> 70 </div> 71 {% endif %} 72 73 <div class="row"> 74 <div class="col-md-offset-1"> 75 <p>{{ comment.content }}</p> 76 </div> 77 </div> 78 </li> 79 {% endfor %} 80 81 </ul> 82 </div> 83 84 <div class="comment"> 85 <p> 86 <label for="tbCommentAuthor">昵称: </label> 87 <input type="text" id="tbCommentAuthor" class="author" disabled size="50" 88 value="{{ request.user.username }}"> 89 </p> 90 91 <div> 92 <p><label for="">评论内容:</label></p> 93 <p><textarea type="text" id="comment_area" cols="60" rows="5"></textarea></p> 94 <input type="button" id="comment_submit_btn" class="btn btn-primary" value="提交"> 95 <input type="button" id="comment_cancel" class="btn btn-primary" value="取消"> 96 </div> 97 </div> 98 {# 这里的这个标签不为渲染任何效果,纯粹是为了给我们的js代码需要用到的标签存储变量数据的,仅此而已,所以我们一般都会写到我们的HTML模板的最下方,#} 99 {# 后期如果有更多的数据都可以写到这里来,目前仅仅这一个变量所以就写它一个,#} 100 <div class="info" username="{{ request.user.username }}" article_pk="{{ article_obj.pk }}"></div> 101 {% csrf_token %} 102 103 <script> 104 {# $(".digg").click(function(){#} 105 {# alert('hello')#} 106 {# });#} 107 //点赞按钮 108 $(".digg").click(function () { 109 if ("{{ request.user.username }}") { 110 var is_up = $(this).hasClass("diggit");//我们在这里定义了如果div里面有这个class属性的话它就是true否则就是false,因为我们的is_up是bool类型 111 {# console.log(is_up);#} 112 $.ajax({ 113 url: "/step/dig/", 114 type: "post", 115 data: { 116 article_id:{{ article_obj.pk }}, 117 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), 118 is_up: is_up 119 }, 120 success: function (data) { 121 var error_info; //声明一个变量共后续使用 122 if (data.state) { //如果点赞操作成功的话就走这一步 123 if (is_up) { 124 var vae = parseInt($("#digg_count").text()) + 1; 125 $("#digg_count").text(vae); 126 } 127 else { 128 var val = parseInt($("#bury_count").text()) + 1; 129 $("#bury_count").text(val); 130 } 131 } 132 else { //如果点赞操作失败的话就走这一步 133 if (data.first_updown) { 134 error_info = "已经点赞过了" 135 } else { 136 error_info = "已经踩灭过了" 137 } 138 139 $("#digg_tips").html(error_info).css("color", "red"); 140 141 setTimeout(function () { 142 $("#digg_tips").html("") 143 }, 5000) 144 } 145 } 146 }) 147 } 148 else { 149 alert(123) 150 } 151 }); 152 153 154 //基于ajax绑定提交评论事件{评论楼显示效果} 155 var parent_comment_pk = ""; 156 $("#comment_submit_btn").click(function () { 157 //区分跟评论和子评论基于一个变量parent_comment_pk,我们的评论是分为根评论和子评论的,要区分对待才能顺利提交, 158 // 因为二者是共享的一个事件为了节省代码,所以就在这里把他们区分开进行处理 159 var content = $("#comment_area").val(); //我们在前端模板语言里面只有三个标签是可以调用val()方法的, 160 // 第一个就是input,第二个就是select,第三个是textarea 请务必牢记 161 if (parent_comment_pk) { //子评论如果if判断结果为true就是子评论,否则为根评论 162 var index = content.indexOf("\n"); 163 content = content.slice(index + 1); 164 } else { //根评论 165 var content = $("#comment_area").val(); 166 } 167 168 //清空输入框的内容 169 $("textarea").val(""); 170 {# alert(content);#} 171 172 $.ajax({ //我们的ajax是在click里面的,我们要点击然后才能把数据提交到后端进行处理 173 url: "/step/comment/", 174 type: "post", 175 data: { 176 content: content, 177 article_id: "{{ article_obj.pk }}", 178 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), 179 pd: parent_comment_pk 180 }, 181 success: function (data) { 182 //我们需要在这里把变量的名字都替换掉,然后再浏览器渲染出来效果,如何替换 183 // 这里是ajax内容实现后的返回结果,我们要在这里处理页面渲染问题,我们的变量就只有article_obj是article表格里面的一个object对象,然后就是我们的comment_list是一个集合,里面装的是所有的comment,我们需要一个抓手去通过它找到我们的comment表格里面的对应的字段和值,只要找到了这个东西就可以轻易取出我们需要的数据,content和create_time都轻易能获取到,但是这个抓手从哪儿来 184 var data = JSON.parse(data); 185 var floor_count = $(".list-group .list-group-item").length; 186 var create_time = data.create_time; 187 var content = data.cont; 188 var user = $(".info").attr("username");//我们这样写比直接后端传过来的request.user.username效果要好, 189 // 我们的js代码按照规范来都是需要写到静态文件里面的,如果我们在下面的字符串里面直接写 190 // request.user.username的话(以下简称为it),作为静态文件的js代码就仅仅是把it作为一个字符串进行渲染, 191 // 也就是说它不会有逻辑代码在里面转换成你需要的username,所以我们如果是使用这种方式去写的话,即便作为静态文件, 192 // 我们在渲染的时候也可以从我们的HTML模板里面拿到值,因为我们的静态文件加载的时候就是为了渲染我们的这个HTML模板的, 193 // 而我们的HTML模板里面的逻辑代码则是跟我们的views视图函数紧密相连的,所以,是万无一失的, 194 // 我们以后有很多其他的变量需要在后期用到的话都是可以存入到我们的HTML标签里面的 195 s = '<li class="comment_item list-group-item"><div class="row"><div class="col-md-offset-3"><a href="">#' + floor_count + '楼</a> <span>' + create_time + '</span> <span>' + user + '</span><p><span>' + content + '</span></p></div></div></li>'; 196 $(".comment_list").append(s); 197 198 } 199 }) 200 }); 201 202 203 //绑定回复按钮事件 204 $(".comment_item .reply_btn").click(function () { 205 //获取焦点 206 $("#comment_area").focus(); 207 //设置:@用户名 208 var vae = "@" + $(this).attr("comment_user") + "\n"; 209 $("#comment_area").val(vae); 210 //获取回复评论的主键值 parent_comment_pk 211 parent_comment_pk = $(this).attr("comment_id") 212 }); 213 214 //取消评论内容 215 $("#comment_cancel").click(function () { 216 $("textarea").val(""); 217 }); 218 219 220 //这里把评论树展示出来 221 $(".tree_click").click(function () { 222 $.ajax({ 223 url: "/step/show_tree/" +{{ article_obj.pk }}, 224 success: function (data) { 225 console.log(data); 226 var comment_li = data; 227 var comment_html = ""; 228 $.each(comment_li, function (index, comment) { 229 var nid = comment.nid; 230 var content = comment.content; 231 var pid = comment.parent_comment_id; 232 var username = comment.user__username; 233 var create_time = comment.create_time; 234 comment_html = ' <div class="comment_item" style="margin-left:20px;">' + 235 '<span>' + username + '</span> ' + 236 '<span>' + create_time + '</span> ' + 237 '<span class="content" tree_id=' + nid + '>' + content + '</span>' + 238 '</div>'; 239 240 comment_text = ' <li class="comment_item list-group-item">' + 241 '<div class="row" ><div class="col-md-offset-1">' + 242 '<span>' + create_time + '</span> <span>' + username + '</span> ' + 243 '<div class="pull-right"> ' + 244 '<a class="reply_btn" href="#" comment_id=' + nid + ' ' + 245 'comment_user=' + username + '>回复</a> </div> </div> <div class="row"> ' + 246 '<div class="col-md-offset-1"> <p parent_id=' + nid + '>' + content + '</p> </div> </div> </li>'; 247 248 content_file = ' <div class="row"><div class="col-md-offset-2 well" style="margin-right:30px;">' + 249 '<a href="">@ ' + username + '</a> ' + 250 '<span parent_id=' + nid + '>' + content + '</span> <div class="pull-right"> ' + 251 '<a href="#" class="reply_btn" comment_id=' + nid + ' ' + 252 'comment_user=' + username + '>回复</a> </div> </div></div>'; 253 254 if (pid) { 255 $("[parent_id=" + pid + "]").parent().prepend(content_file); 256 } else { 257 $(".comment_tree").append(comment_text); 258 } 259 {# if(pid){ //有子评论#} 260 {# $("[tree_id="+pid+"]").parent().append(comment_html);#} 261 262 //这里的$('[class="content"]')语法就是找到含有该键值对属性的标签, 263 // 只要是符合条件都可以使用这样的方法去查找,包括我们常用的class属性和id属性都是可以的, 264 // 因为我们的class和id使用频率较高,所以我们一般用更加高效的.或者#去查找, 265 // 其他的情况下我们没有那么高效的api方法就使用这样的方法去查找 266 {# }else{ //没有子评论#} 267 {# $(".comment_tree").append(comment_html);#} 268 {# }#} 269 }) 270 271 } 272 273 274 }) 275 }) 276 277 278 </script> 279 280 {% endblock %}
我们把js代码单独拎出来:

1 //这里把评论树展示出来 2 $(".tree_click").click(function () { 3 $.ajax({ 4 url: "/step/show_tree/" +{{ article_obj.pk }}, 5 success: function (data) { 6 console.log(data); 7 var comment_li = data; 8 var comment_html = ""; 9 $.each(comment_li, function (index, comment) { 10 var nid = comment.nid; 11 var content = comment.content; 12 var pid = comment.parent_comment_id; 13 var username = comment.user__username; 14 var create_time = comment.create_time; 15 16 #这里是没有样式的素版 17 comment_html = ' <div class="comment_item" style="margin-left:20px;">' + 18 '<span>' + username + '</span> ' + 19 '<span>' + create_time + '</span> ' + 20 '<span class="content" tree_id=' + nid + '>' + content + '</span>' + 21 '</div>'; 22 23 # 这里是bootstrap版本的 24 comment_text = ' <li class="comment_item list-group-item">' + 25 '<div class="row" ><div class="col-md-offset-1">' + 26 '<span>' + create_time + '</span> <span>' + username + '</span> ' + 27 '<div class="pull-right"> ' + 28 '<a class="reply_btn" href="#" comment_id=' + nid + ' ' + 29 'comment_user=' + username + '>回复</a> </div> </div> <div class="row"> ' + 30 '<div class="col-md-offset-1"> <p parent_id=' + nid + '>' + content + '</p> </div> </div> </li>'; 31 32 content_file = ' <div class="row"><div class="col-md-offset-2 well" style="margin-right:30px;">' + 33 '<a href="">@ ' + username + '</a> ' + 34 '<span parent_id=' + nid + '>' + content + '</span> <div class="pull-right"> ' + 35 '<a href="#" class="reply_btn" comment_id=' + nid + ' ' + 36 'comment_user=' + username + '>回复</a> </div> </div></div>'; 37 38 if (pid) { 39 $("[parent_id=" + pid + "]").parent().prepend(content_file); 40 } else { 41 $(".comment_tree").append(comment_text); 42 } 43 {# if(pid){ //有子评论#} 44 {# $("[tree_id="+pid+"]").parent().append(comment_html);#} 45 46 //这里的$('[class="content"]')语法就是找到含有该键值对属性的标签, 47 // 只要是符合条件都可以使用这样的方法去查找,包括我们常用的class属性和id属性都是可以的, 48 // 因为我们的class和id使用频率较高,所以我们一般用更加高效的.或者#去查找, 49 // 其他的情况下我们没有那么高效的api方法就使用这样的方法去查找 50 {# }else{ //没有子评论#} 51 {# $(".comment_tree").append(comment_html);#} 52 {# }#} 53 }) 54 55 } 56 57 58 }) 59 })
有一个小bug还是没有解决掉,就是我们的评论树在使用ajax展开的时候,我们绑定的是点击事件,我们只能点击一次就可以看到预期的效果,但是如果我们点击超过一次的话,就会重复显示内容,这一点还是很可怕的,我们的用户不可能就只是点击一次,这里需要怎么解决?