立即注册 登录
彼岸网 返回首页

天香公主的个人空间 http://www.bian-wang.com/discuz/?10005 [收藏] [复制] [分享] [RSS] txgz999@yahoo.com

日志

Discuz系统中的AJAX功能

热度 3已有 356 次阅读2017-10-3 12:29 AM |个人分类:Discuz

Discuz的很多网页都使用了AJAX处理方式,即在客户端网页上用javascript代码和网站服务器联络并按需更新网页上的局部内容。过去我讨论过在客户端Discuz软件提供的支持 AJAX 的函数 ajaxget (链接), 它被用于向服务器发送get请求,适用于不改变储存数据的情形。ajaxget函数可谓是Discuz提供的一个类函数Ajax的一个包装,这个类函数的实现则是基于AJAX普适的XMLHttpRequest函数。本文讨论下另一个函数 ajaxpost,它被用于向服务器发post请求,适用于将用户填写的数据提交给服务器储存。虽然那个XMLHttpRequest函数也支持向服务器发post请求,但这个ajaxpost函数并没有利用它,而是用了一种更古老的办法:用隐藏的iframe来和服务器交换数据。

这种方法很简单,当我们用表单(form)来向服务器发送请求,那个服务器返回的信息显示在哪里呢?最简单的情形下,服务器返回的html内容就显示在浏览器里,替代了原来显示的内容。但是表单有个target属性可以设置,可以将它设成网页上一个iframe的名字,这样的话,服务器返回的内容就不替代原来网页的内容,而是显示在这个iframe里了。下图显示的是一个例子,服务器提供了将字符串改变成大写的服务,网页上利用这个服务将用户填写的字符串的大写形式显示在右端的iframe里:

那么如果我们不想将服务器返回的内容显示在iframe里,而要将它显示在其它地方或派其它用途该怎么办呢?那也不难,我们可以将这个iframe隐藏起来,然后去监听它的onload事件,当该事件发生时,从iframe里将它的内容取出来(因为iframe是隐藏的,所以它的内容也没直接显示),然后再放在想显示的地方去。如下图所示:


Discuz的AJAX功能就是建立在这样一个简单的原理上,但又在它的基础上为使用这个方法提供了各种便利,比如
1) 使用者不需在网页上放上一个iframe,ajaxpost函数在需要时会自动添加它。
2) ajaxpost和服务器端的函数showmessage对传递的内容有一定的约定,比如这样服务器端就能很方便的同时将文字和要在客户端运行的代码同时传递到客户端。
3) ajaxpost可以将返回的文字部分放入指定的DOM元素中。同时如果有必要的话,接着调用网页上一个指定的函数来进一步处理。


实例分析:日志页上的添加评论功能

评论提交前 评论提交后
<form id="quickcommentform_{$id}" action="home.php?mod=spacecp&ac=comment" method="post" autocomplete="off" onsubmit="ajaxpost('quickcommentform_{$id}', 'return_qcblog_$id');doane(event);"> <input type="hidden" name="handlekey" value="qcblog_{$id}" /> <span id="return_qcblog_{$id}"></span> ... </form> <script type="text/javascript"> function succeedhandle_qcblog_$id(url, msg, values) { if(values['cid']) { comment_add(values['cid']); } else { $('return_qcblog_{$id}').innerHTML = msg; } ... </script> 1)显示添加评论的表单
home.php
  -> module/home/home_space.php
     -> include/home/space_blog.php
        -> template/default/home/space_blog_view.htm
表单的onsubmit事件处理调用了ajaxpost函数和doane函数,ajaxpost接收了两个参数,第一个是表单的id,函数在提交表单时需要该id;第二个是一个DOM元素的id,这个DOM元素被用来显示服务器返回的信息,如'操作成功'。鉴于提交表单是在ajaxpost里完成的,所以接着调用了doane来取消表单的缺省处理方式。
2)处理提交的表单数据,返回新加入的评论ID
home.php
  -> module/home/home_spacecp.php
     -> include/home/spacecp_comment.php
        -> add_comment in function_comment.php
        -> showmessage in function_core.php
           -> dshowmessage in function_message.php
              -> template/default/common/showmessage.htm
服务器收到提交的评论后,将它储存在数据库里,并返回该评论的ID。下面是返回的数据的一个例子,其中的cid就是该评论的ID。 <root> 操作成功 <script type="text/javascript" reload="1">if(typeof succeedhandle_qcblog_22=='function') {succeedhandle_qcblog_22('http://localhost:14908/./home.php?mod=space&uid=1&do=blog&id=22', '操作成功 ', {'cid':'16'});}</script> </root> 3)获取新加入的评论,并显示在网页上
comment_add in home.js
  -> home.php
     -> module/home/home_misc.php
        -> include/misc/misc_ajax.php
           -> template/default/home/misc_ajax.htm
ajaxpost收到这样的返回数据后,会将它分解成信息部分和代码部分,信息放在传递给它的第二个变量值所指向的那个DOM元素里,然后执行代码。这个代码只是调用一个事先给定的函数并将它所需的信息,即刚保存的评论的ID,作为参数的组成部分交给它。它就能从服务器里得到该评论的内容,并将它加入评论列表。但是服务器端怎么知道应该调用客户端的哪个函数呢?这里有个约定:这个函数的名字必须以succeedhandle_开头,接着来的那部分,作为隐藏元素handlekey的值,在提交表单时告诉了服务器端。向服务器提取评论内容用的是一个叫Ajax的类函数,前面提到的ajaxpost也是用这个类函数来提供AJAX功能的。所以整个过程其实用了两次AJAX。

给文章和专题添加AJAX提交评论的功能
虽然日志上的评论是用AJAX提交的,但是门户里的文章和专题页上的评论提交却需要更新整个网页,有网友建议何不让文章和专题页上的评论也用AJAX提交。下面就讨论下如何在文章和专题页里模仿日志上的功能。之所以同时讨论文章和专题是因为两者均属于门户,有很多代码是共享的。在下面的叙述中,蓝色的文件名代表该文件包含有修改过的代码。

1。添加评论
1)显示添加评论的表单
日志 文章和专题 注记
home.php portal.php  
  -> home_space.php   -> portal_view.php (文章); portal_topic.php (专题)
    -> space_blog.php  
      ->view.htm (文章); portal_topic_content.htm (专题)  
      -> space_blog_view.htm       ->portal_comment.htm 这个模板包含了用于提交新评论的表单。要对它添加onsubmit属性在其中调用ajaxpost,还要添加被ajaxpost调用的javascript函数succeedhandle_$_GET['handlekey']。同时将表单搬到了评论列表comment_ul的外面,改动后这部分就和space_blog_view.htm里相应部分的结构完全一致了。做此改动的原因是不然提交评论后验证码会消失。
2)处理提交的表单数据,返回新加入的评论ID
日志 文章和专题 注记
home.php portal.php  
  -> home_spacecp.php   -> portal_portalcp.php  
    -> spacecp_comment.php     -> portalcp_comment.php 修改了函数addportalarticlecomment,使之和add_comment一样返回新评论的ID。注意函数addportalarticlecomment亦为文件source/include/spacecp/spacecp_share.php所使用,所以这个改动也影响到了它。
      -> add_comment in function_comment.php       -> addportalarticlecomment in function_portalcp.php
3)获取新加入的评论,并显示在网页上
日志 文章和专题 注记
comment_add in home.js portal_comment_add in portal.js portal_comment_add是新加的函数,类似于comment_add,用于向服务器发送获取新评论内容的请求,并将结果加到网页上去。之所以不沿用函数名comment_add是因为文章页同时加入了portal.js和home.js,故而同名的话会被home.js里的同名函数所覆盖。
  -> home.php   -> portal.php  
    -> home_misc.php    
      -> misc/misc_ajax.php     -> portal_comment.php 新加一段:当提交的数据里有评论ID时,从数据库里取出评论内容,并交给下面的模板显示。
        -> misc_ajax.htm       -> misc_ajax.htm 在该模板文件里加了个op=portalcomment的情形,用来返回文章和专题的一个评论的显示区域。

2。修改和删除评论
1)网页上显示评论
日志 文章和专题 注记
home.php portal.php  
  -> home_space.php   -> portal_view.php (文章); portal_topic.php (专题)  
    -> space_blog.php  
      ->view.htm (文章); portal_topic_content.htm (专题)  
      -> space_blog_view.htm       ->portal_comment.htm  
        -> space_comment_li.htm         ->comment_li.htm  
2)显示修改评论或确认删除的对话框
日志 文章和专题 注记
home.php portal.php  
  -> home_spacecp.php   -> portal_portalcp.php  
    -> spacecp_comment.php     -> portalcp_comment.php  
      -> spacecp_comment.htm       -> portalcp_comment.htm 这个模板包含了用于提交修改后的评论或删除评论的请求的表单。要对它添加onsubmit属性在其中调用ajaxpost,还要添加被ajaxpost调用的javascript函数succeedhandle_$_GET['handlekey']
3)处理保存修改的评论或删除评论的请求
日志 文章和专题 注记
home.php portal.php  
  -> home_spacecp.php   -> portal_portalcp.php  
    -> spacecp_comment.php     -> portalcp_comment.php 在保存评论或删除评论后调用showmessage时提供了该评论的ID
4)在网页上更新评论内容
日志 文章和专题 注记
comment_edit & comment_delete in home.js portal_comment_edit & portal_comment_delete in portal.js portal_comment_edit 类似于comment_edit,用于向服务器发送获取修改后的评论内容的请求,并将结果加到网页上去。portal_comment_delete 类似于comment_delete,用于隐藏被删除的评论。
  -> home.php   -> portal.php  
    -> home_misc.php    
      -> misc/misc_ajax.php     -> portal_comment.php 共用第一部分的第三段的修改。
        -> misc_ajax.htm       -> misc_ajax.htm 共用第一部分的第三段的修改。


总结一下:共需要修改下列8个文件,具体修改见下载文件包。
  1. source/module/portal/portal_comment.php
  2. source/include/portalcp/portalcp_comment.php
  3. source/include/spacecp/spacecp_share.php
  4. source/function/function_portalcp.php
  5. template/default/portal/portal_comment.htm
  6. template/default/portal/portalcp_comment.htm
  7. template/default/home/misc_ajax.htm
  8. static/js/portal.js
下载文件: http://www.bian-wang.com/discuz/data/userupload/10005/ajaxpost_portal_comment.zip

注:文中的插图是用在线软件gliffy画的。

发表评论 评论 (20 个评论)

回复 天香公主 2017-11-18 09:42 AM
sesemule: 有空我会尝试一下(^_^) 最近比较忙……
嗯,看看博客里是怎么置顶的,可以在代码里用stick做为搜索词搜一下相关的代码
回复 sesemule 2017-11-18 05:21 AM
天香公主: 这可以象博客那里一样如法炮制,你不想自己尝试下?
有空我会尝试一下(^_^) 最近比较忙……
回复 天香公主 2017-11-15 10:54 PM
sesemule: 天香,能不能加个评论置顶功能?方便作者把最有价值的评论放在最显眼的位置
这可以象博客那里一样如法炮制,你不想自己尝试下?
回复 sesemule 2017-11-14 08:50 PM
天香公主: 谢谢,我明白你的意思了,但查了下没觉得少了东西。文件portal_comment.htm最后确有两个</div>,但外面那个</div>我并没有涉及,我的文件5.txt讲的是关于portal_ ...
天香,能不能加个评论置顶功能?方便作者把最有价值的评论放在最显眼的位置
回复 天香公主 2017-10-12 09:04 PM
谢谢,我明白你的意思了,但查了下没觉得少了东西。文件portal_comment.htm最后确有两个</div>,但外面那个</div>我并没有涉及,我的文件5.txt讲的是关于portal_comment.htm中下面这个区域的修改:

<div id="comment_ul" class="bm_c">
...
</div>

这里的这个</div>是原文件里的倒数第二个</div>,我将它往前面搬了点。

至于原文件的最后一个</div>,在5.txt讨论的区域外,所以应该视为不改动,而不能理解为删去。
回复 ladyff 2017-10-11 08:44 PM
天香公主: 你可以把图片上传到相册里,再将上传后的图片的url写在这里,谢谢!
错位效果

http://www.bian-wang.com/discuz/data/attachment/album/201710/11/204247f72saqasy04hy42r.png


文件对比,div少了一个
http://www.bian-wang.com/discuz/data/attachment/album/201710/11/204246yqzvn13558zpdq5h.png
回复 天香公主 2017-10-10 08:23 PM
ladyff: 似乎评论里发不了图片和附件
你可以把图片上传到相册里,再将上传后的图片的url写在这里,谢谢!
回复 ladyff 2017-10-10 01:22 AM
天香公主: 嗯,你看得很仔细,的确是将form移到了comment_ul外了,如果你和space_blog_view.htm的相应部分比较一下就会发现,改动后的代码和日志页的结构就一致了。做此改 ...
似乎评论里发不了图片和附件
回复 天香公主 2017-10-9 11:45 PM
ladyff: 另外一个小问题是 似乎是门户评论发送后没有任何反馈?完全不知道有没有发出去,点了好几次回到页面一看原来发了出去。
建议点击“评论”后,用javascript清空评 ...
我记得发送评论后,评论框是清空的。这就是评论已发出的标志。如果你的评论框没清空的话,再检查下是否改全了。

新评论加在了评论列表的最上面,当然也可以象日志页那样加在最后,那样的话也许能更清楚的看到新加的评论。要想那样的话就是portal_comment_add里一个很小的改动。
回复 天香公主 2017-10-9 11:33 PM
ladyff: 按照提供的文件测试了下,
template/default/portal/portal_comment.htm

这个修改有点小问题,原模版最后2行的</div></div>被你删掉了,然后添加了一个</div>到 ...
嗯,你看得很仔细,的确是将form移到了comment_ul外了,如果你和space_blog_view.htm的相应部分比较一下就会发现,改动后的代码和日志页的结构就一致了。做此改动的原因是不然提交评论后验证码会消失。

至于你说的改动后出现的问题,我没发现。你能否截个图把你认为有问题的部分标出来让我看看,谢谢。
回复 ladyff 2017-10-9 11:25 PM
另外一个小问题是 似乎是门户评论发送后没有任何反馈?完全不知道有没有发出去,点了好几次回到页面一看原来发了出去。
建议点击“评论”后,用javascript清空评论框,然后跳到到评论第一条,可以说就很完美了
回复 ladyff 2017-10-9 11:23 PM
天香公主: 谢谢指正
按照提供的文件测试了下,
template/default/portal/portal_comment.htm

这个修改有点小问题,原模版最后2行的</div></div>被你删掉了,然后添加了一个</div>到第19行,这样用默认模版的评论框会出现错位,导致右侧的分类信息跑到评论框下。最好还是不要动div的结构,就比较完美了

<div id="comment" class="bm">
        <div class="bm_h cl">
                <!--{if !$data[htmlmade]}-->
                        <!--{if $data[commentnum]}-->
                                <a href="javascript:;" class="y xi2" onclick="location.href=location.href.replace(/(\#.*)/, '')+'#cform';$('message').focus();return false;">{lang post_comment}</a>
                        <!--{/if}-->
                <!--{else}-->
                        <a href="$common_url#cform" class="y xi2">{lang post_comment}</a>
                <!--{/if}-->
                <h3>{lang latest_comment}</h3>
        </div>
        <div id="comment_ul" class="bm_c">
        <!--{loop $commentlist $comment}-->
        <!--{template portal/comment_li}-->
        <!--{if !empty($aimgs[$comment[cid]])}-->
        <script type="text/javascript" reload="1">aimgcount[{$comment[cid]}] = [<!--{echo implode(',', $aimgs[$comment[cid]]);}-->];attachimgshow($comment[cid]);</script>
        <!--{/if}-->
        <!--{/loop}-->
                <!--{if !empty($pricount)}-->
                        <p class="mtn mbn y">{lang hide_portal_comment}</p>
                <!--{/if}-->
                <!--{if $data[commentnum]}--><p class="ptm pbm"><a href="$common_url" class="xi2">{lang view_all_comments}(<em id="_commentnum">$data[commentnum]</em>)</a></p><!--{/if}-->
                <!--{if !$data[htmlmade]}-->
                        <form id="cform" name="cform" action="$form_url" method="post" autocomplete="off" onsubmit="ajaxpost('cform', 'return_cf');doane(event);">
                                <div class="tedt">
                                        <div class="area">
                                                <textarea name="message" rows="3" class="pt" id="message" onkeydown="ctrlEnter(event, 'commentsubmit_btn');"></textarea>
                                        </div>
                                </div>

                                <!--{if $secqaacheck || $seccodecheck}-->
                                        <!--{block sectpl}--><sec> <span id="sec<hash>" onclick="showMenu(this.id);"><sec></span><div id="sec<hash>_menu" class="p_pop p_opt" style="display:none"><sec></div><!--{/block}-->
                                        <div class="mtm"><!--{subtemplate common/seccheck}--></div>
                                <!--{/if}-->
                                <!--{if !empty($topicid) }-->
                                        <input type="hidden" name="referer" value="$topicurl#comment" />
                                        <input type="hidden" name="topicid" value="$topicid">
                                <!--{else}-->
                                        <input type="hidden" name="portal_referer" value="$viewurl#comment">
                                        <input type="hidden" name="referer" value="$viewurl#comment" />
                                        <input type="hidden" name="id" value="$data[id]" />
                                        <input type="hidden" name="idtype" value="$data[idtype]" />
                                        <input type="hidden" name="aid" value="$aid">
                                <!--{/if}-->
                <input type="hidden" name="handlekey" value="fc" />
                                <input type="hidden" name="formhash" value="{FORMHASH}">
                                <input type="hidden" name="replysubmit" value="true">
                                <input type="hidden" name="commentsubmit" value="true" />
                                <p class="ptn"><button type="submit" name="commentsubmit_btn" id="commentsubmit_btn" value="true" class="pn"><strong>{lang comment}</strong></button>
                  <span id="return_cf"></span>
                </p>
                        </form>
            <script type="text/javascript">
                                function succeedhandle_fc(url, msg, values) {
                                    if(values['cid']) {
                                        portal_comment_add(values['cid']);
                                    } else {
                                        $('return_cf').innerHTML = msg;
                                    }
                                }
            </script>
                <!--{/if}-->
        </div>
</div>
回复 天香公主 2017-10-9 09:59 PM
ladyff: readme.txt里第一个有个错误
不是source/module/portal_comment.php,应该是
source/module/portal/portal_comment.php
谢谢指正
回复 ladyff 2017-10-9 09:52 PM
readme.txt里第一个有个错误
不是source/module/portal_comment.php,应该是
source/module/portal/portal_comment.php
回复 天香公主 2017-10-4 05:50 AM
牛肉炖土豆: 中秋快乐!
谢谢,我也祝您中秋节快乐!
回复 牛肉炖土豆 2017-10-3 10:10 PM
天香公主:          
中秋快乐!
回复 天香公主 2017-10-3 08:02 PM
牛肉炖土豆: 测试成功,未发现BUG,完美实现无刷新评论,而且还支持编辑,删除,引用回复的无刷新,真是太好了,谢谢天香   ...
      
回复 牛肉炖土豆 2017-10-3 07:45 PM
测试成功,未发现BUG,完美实现无刷新评论,而且还支持编辑,删除,引用回复的无刷新,真是太好了,谢谢天香
回复 天香公主 2017-10-3 12:40 PM
牛肉炖土豆: 哇,看似简单的功能,原来涉及到这么多,天香公主费心了!
我一直搞不明白AJAX功能,借此学习一下,马上测试一下看看 ...
哈,其实添加的代码不多。
回复 牛肉炖土豆 2017-10-3 06:41 AM
哇,看似简单的功能,原来涉及到这么多,天香公主费心了!
我一直搞不明白AJAX功能,借此学习一下,马上测试一下看看

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

小黑屋|Archiver|彼岸网  

Powered by Discuz! X3.1 © 2001-2014 Comsenz Inc.
GMT-4, 2017-11-25 03:50 AM , Processed in 0.066409 second(s), 19 queries.

返回顶部