国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

個(gè)人網(wǎng)站留言頁(yè)面(前端jQuery編寫、后臺(tái)php讀寫MySQL)

2019-11-20 10:10:31
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

首先,上個(gè)人網(wǎng)站的留言頁(yè)面,大家可以看看效果:留言板

前端為了省事,使用jQuery編寫,后臺(tái)使用php簡(jiǎn)單讀寫MySQL數(shù)據(jù)庫(kù)。

數(shù)據(jù)庫(kù)設(shè)計(jì)和實(shí)現(xiàn)思路

數(shù)據(jù)庫(kù)創(chuàng)建了一個(gè)表:comments,結(jié)構(gòu)如下圖:

全部評(píng)論(包括文章評(píng)論回復(fù),留言板)都寫在同一張表中,不同的評(píng)論區(qū)用字段belong區(qū)分

同一個(gè)評(píng)論區(qū)里,parent為0表示為評(píng)論,parent為某值時(shí)表示為哪個(gè)評(píng)論的回復(fù),思路不復(fù)雜。

注意,這里并不講CSS,大家根據(jù)自己的需要定制,現(xiàn)在開始封裝:

定下功能

我們根據(jù)自己的需要定下功能,首先我的網(wǎng)站并沒(méi)有實(shí)現(xiàn)消息提醒,即時(shí)通訊的功能,所以評(píng)論回復(fù)并不會(huì)提示站長(zhǎng)或者用戶,只會(huì)對(duì)留言區(qū)產(chǎn)生效果,所以我們只要簡(jiǎn)單實(shí)現(xiàn)以下功能:

1、顯示評(píng)論列表

2、能夠提交評(píng)論

3、進(jìn)行回復(fù)

評(píng)論類

我們將評(píng)論的功能封裝成一個(gè)類,通過(guò)實(shí)例化就能創(chuàng)建不同的評(píng)論區(qū),所以不難想到,

實(shí)例化的時(shí)候我們需要傳入的參數(shù)可能有:評(píng)論區(qū)的id、獲取評(píng)論的php地址,提交評(píng)論的php地址。

所以我們可以猜想實(shí)例化評(píng)論區(qū)的代碼可能為:

var oCmt = new Comment({ parent: $('#box'),      //你想要將這個(gè)評(píng)論放到頁(yè)面哪個(gè)元素中 id: 0, getCmtUrl: './php/getcomment.php', setCmtUrl: './php/comment.php'})

當(dāng)然,我是在Comment類上定義一個(gè)靜態(tài)方法

Comment.allocate({ parent: $('#box'), id: 0, getCmtUrl: './php/getcomment.php', setCmtUrl: './php/comment.php'})

大同小異,只是初始化的地方不同而已

構(gòu)造函數(shù)

function Comment(options){ this.belong = options.id; this.getCmtUrl = options.getCmtUrl; this.setCmtUrl = options.setCmtUrl; this.lists = []; this.keys = {}; this.offset = 5;}var fn = Comment.prototype;Comment.allocate = function(options){ var oCmt = new Comment(options); if (oCmt.belong == undefined || !oCmt.getCmtUrl || !oCmt.setCmtUrl) {  return null; }; oCmt.init(options); return oCmt;};

里面的變量和方法我們慢慢解釋,如果你不定義一個(gè)allocate方法,那么可以寫成:

function Comment(options){ this.belong = options.id; this.getCmtUrl = options.getCmtUrl; this.setCmtUrl = options.setCmtUrl; this.lists = []; this.keys = {}; this.offset = 5; if (this.belong == undefined || !this.getCmtUrl || !this.setCmtUrl) {  return null; }; this.init(options)}var fn = Comment.prototype;

變量先不說(shuō),像我都是先寫功能函數(shù),然后需要添加屬性變量再回頭來(lái)添加,我們只需要看到構(gòu)造函數(shù)最后執(zhí)行了:

this.init(options)

從名字可以看出是初始化函數(shù)。

init函數(shù)

fn.init = function (options) { //初始化node this.initNode(options); //將內(nèi)容放進(jìn)容器 this.parent.html(this.body); //初始化事件 this.initEvent(); //獲取列表 this.getList();};

fn為Comment.prototype,只說(shuō)一次,下面就不再說(shuō)了。

初始化就是有4個(gè)工作要做,從代碼注釋可以看出,現(xiàn)在一個(gè)一個(gè)講解

initNode函數(shù)

從名字可以看出主要初始化節(jié)點(diǎn)或者緩存dom

fn.initNode = function(options){ //init wrapper box if (!!options.parent) {  this.parent = options.parent[0].nodeType == 1 ? options.parent : $('#' + options.parent); }; if (!this.parent) {  this.parent = $('div');  $('body').append(this.parent); } //init content this.body = (function(){  var strHTML = '<div class="m-comment">' +       '<div class="cmt-form">' +        '<textarea class="cmt-text" placeholder="歡迎建議,提問(wèn)題,共同學(xué)習(xí)!"></textarea>' +        '<button class="u-button u-login-btn">提交評(píng)論</button>' +       '</div>' +       '<div class="cmt-content">' +        '<div class="u-loading1"></div>' +        '<div class="no-cmt">暫時(shí)沒(méi)有評(píng)論</div>' +        '<ul class="cmt-list"></ul>' +        '<div class="f-clear">' +         '<div class="pager-box"></div>' +        '</div>' +       '</div>' +      '</div>';  return $(strHTML); })(); //init other node this.text = this.body.find('.cmt-text').eq(0); this.cmtBtn = this.body.find('.u-button').eq(0); this.noCmt = this.body.find('.no-cmt').eq(0); this.cmtList = this.body.find('.cmt-list').eq(0); this.loading = this.body.find('.u-loading1').eq(0); this.pagerBox = this.body.find('.pager-box').eq(0);};

代碼中我們可以看出:

this.parent : 保存的是容器節(jié)點(diǎn)
this.body : 保存的是評(píng)論區(qū)的html
this.text : 保存的是評(píng)論的textarea元素
this.cmtBtn : 保存的是提交按鈕
this.noCmt : 保存的是沒(méi)有評(píng)論時(shí)的文字提醒
this.cmtList : 保存的是列表的容器
this.loading : 保存的是加載列表時(shí)的loading GIF圖片
this.pagerBox : 需要分頁(yè)時(shí)的分頁(yè)器容器

js上沒(méi)有難點(diǎn),都是一些jQuery的方法

將內(nèi)容放進(jìn)容器中

this.parent.html(this.body)

這個(gè)沒(méi)什么好講的,很簡(jiǎn)單,這時(shí)我們的評(píng)論組件應(yīng)該在頁(yè)面顯示了,只是現(xiàn)在沒(méi)有加載評(píng)論列表,也不能評(píng)論,下面先講加載評(píng)論列表

getList 函數(shù)

首先是初始化列表,清空,顯示加載gif圖,隱藏沒(méi)有評(píng)論的提醒字樣,做好準(zhǔn)備就發(fā)起ajax請(qǐng)求。

思路是用php將該評(píng)論區(qū)的留言全部弄下來(lái),在前端再來(lái)整理,ajax請(qǐng)求為:

fn.resetList = function(){ this.loading.css('display', 'block') this.noCmt.css('display', 'none'); this.cmtList.html('');};fn.getList = function(){ var self = this; this.resetList(); $.ajax({  url: self.getCmtUrl,  type: 'get',  dataType: 'json',  data: { id: self.belong },  success: function(data){   if(!data){    alert('獲取評(píng)論列表失敗');    return !1;   };   //整理評(píng)論列表   self.initList(data);   self.loading.css('display', 'none');   //顯示評(píng)論列表   if(self.lists.length == 0){    //暫時(shí)沒(méi)有評(píng)論    self.noCmt.css('display', 'block');   }else{    //設(shè)置分頁(yè)器    var total = Math.ceil(self.lists.length / self.offset);    self.pager = new Pager({     index: 1,     total: total,     parent: self.pagerBox[0],     onchange: self.doChangePage.bind(self),     label:{      prev: '<',      next: '>'     }    });   }  },  error: function(){   alert('獲取評(píng)論列表失敗');  } });};

get形式,然后傳送id過(guò)去,得到了的數(shù)據(jù)希望是列表數(shù)組。

php的內(nèi)容不講,下面貼出sql語(yǔ)句:

$id = $_GET['id'];$query = "select * from comments where belong=$id order by time";...$str = '[';foreach ($result as $key => $value) {  $id = $value['id'];  $username = $value['username']; $time = $value['time']; $content = $value['content']; $parent = $value['parent']; $str .= <<<end  {   "id" : "{$id}",   "parent" : "{$parent}",      "username" : "{$username.'",   "time" : "{$time}",   "content" : "{$content}",   "response" : []  }end;} $str = substr($str, 0, -1); $str .= ']'; echo $str;

獲得的是json字符串,jQuery的ajax可以將它轉(zhuǎn)為json數(shù)據(jù),獲得的數(shù)據(jù)如下:

如果加載成功,那么我們得到的是一堆的數(shù)據(jù),我們現(xiàn)在是在success回調(diào)函數(shù)里,數(shù)據(jù)需要整理,才能顯示,因?yàn)楝F(xiàn)在所有的評(píng)論回復(fù)都屬于同一層。

initList 函數(shù)

fn.initList = function (data) { this.lists = []; //保存評(píng)論列表 this.keys = {}; //保存評(píng)論id和index對(duì)應(yīng)表 var index = 0; //遍歷處理 for(var i = 0, len = data.length; i < len; i++){  var t = data[i],   id = t['id'];  if(t['parent'] == 0){   this.keys[id] = index++;   this.lists.push(t);  }else{   var parentId = t['parent'],    parentIndex = this.keys[parentId];   this.lists[parentIndex]['response'].push(t);  } };};

我的思路就是:this.lists放的都是評(píng)論(parent為0的留言),通過(guò)遍歷獲取的數(shù)據(jù),如果parent為0,就push進(jìn)this.lists;否則parent不為0表示這是個(gè)回復(fù),就找到對(duì)應(yīng)的評(píng)論,把該回復(fù)push進(jìn)那條評(píng)論的response中。

但是還有個(gè)問(wèn)題,就是因?yàn)閕d是不斷增長(zhǎng)的,可能中間有些評(píng)論被刪除了,所以id和index并不一定匹配,所以借助this.keys保存id和index的對(duì)應(yīng)關(guān)系。

遍歷一遍就能將所有的數(shù)據(jù)整理好,并且全部存在了this.lists中,接下來(lái)剩下的事情就是將數(shù)據(jù)變成html放進(jìn)頁(yè)面就好了。

//顯示評(píng)論列表if(self.lists.length == 0){ //暫時(shí)沒(méi)有評(píng)論 self.noCmt.css('display', 'block');}else{ //設(shè)置分頁(yè)器 var total = Math.ceil(self.lists.length / self.offset); self.pager = new Pager({  index: 1,  total: total,  parent: self.pagerBox[0],  onchange: self.doChangePage.bind(self),  label:{   prev: '<',   next: '>'  } });}

這是剛才ajax,success回調(diào)函數(shù)的一部分,這是在整理完數(shù)據(jù)后,如果數(shù)據(jù)為空,那么就顯示“暫時(shí)沒(méi)有評(píng)論”。

否則,就設(shè)置分頁(yè)器,分頁(yè)器我直接用了之前封裝的,如果有興趣可以看看我之前的文章:

面向?qū)ο螅悍猪?yè)器封裝

簡(jiǎn)單說(shuō)就是會(huì)執(zhí)行一遍onchange函數(shù),默認(rèn)頁(yè)數(shù)為1,保存在參數(shù)obj.index中

fn.doChangePage = function (obj) { this.showList(obj.index);};

showList函數(shù)

fn.showList = (function(){ /* 生成一條評(píng)論字符串 */ function oneLi(_obj){  var str1 = '';  //處理回復(fù)  for(var i = 0, len = _obj.response.length; i < len; i++){   var t = _obj.response[i];   t.content = t.content.replace(//</;/g, '<');   t.content = t.content.replace(//>/;/g, '>');   str1 += '<li class="f-clear"><table><tbody><tr><td>' +    '<span class="username">' + t.username + ':</span></td><td>' +    '<span class="child-content">' + t.content + '</span></td></tr></tbody></table>' +    '</li>'  }  //處理評(píng)論  var headImg = '';  if(_obj.username == "kang"){   headImg = 'kang_head.jpg';  }else{   var index = Math.floor(Math.random() * 6) + 1;   headImg = 'head' + index + '.jpg'  }  _obj.content = _obj.content.replace(//</;/g, '<');  _obj.content = _obj.content.replace(//>/;/g, '>');  var str2 = '<li class="f-clear">' +   '<div class="head g-col-1">' +   '<img src="./img/head/' + headImg + '" width="100%"/>' +   '</div>' +   '<div class="content g-col-19">' +   '<div class="f-clear">' +   '<span class="username f-float-left">' + _obj.username + '</span>' +   '<span class="time f-float-left">' + _obj.time + '</span>' +   '</div>' +   '<span class="parent-content">' + _obj.content + '</span>' +   '<ul class="child-comment">' + str1 + '</ul>' +   '</div>' +   '<div class="respone-box g-col-2 f-float-right">' +   '<a href="javascript:void(0);" class="f-show response" data-id="' + _obj.id + '">[回復(fù)]</a>' +   '</div>' +   '</li>';  return str2; }; return function (page) {  var len = this.lists.length,   end = len - (page - 1) * this.offset,   start = end - this.offset < 0 ? 0 : end - this.offset,   current = this.lists.slice(start, end);  var cmtList = '';  for(var i = current.length - 1; i >= 0; i--){   var t = current[i],    index = this.keys[t['id']];   current[i]['index'] = index;   cmtList += oneLi(t);  }  this.cmtList.html(cmtList); };})();

這個(gè)函數(shù)的參數(shù)為page,就是頁(yè)數(shù),我們根據(jù)頁(yè)數(shù),截取this.lists的數(shù)據(jù),然后遍歷生成html。

html模板我是用字符串連接起來(lái)的,看個(gè)人喜好。

生成后就 this.cmtList.html(cmtList);這樣就顯示列表了,效果圖看最開始。

現(xiàn)在需要的功能還有評(píng)論回復(fù),而init函數(shù)中也只剩下最后一個(gè)initEvent

initEvent 函數(shù)

fn.initEvent = function () { //提交按鈕點(diǎn)擊 this.cmtBtn.on('click', this.addCmt.bind(this, this.cmtBtn, this.text, 0)); //點(diǎn)擊回復(fù),點(diǎn)擊取消回復(fù),點(diǎn)擊回復(fù)中的提交評(píng)論按鈕 this.cmtList.on('click', this.doClickResponse.bind(this));};

上面截圖來(lái)自我的個(gè)人網(wǎng)站,當(dāng)我們點(diǎn)擊回復(fù)時(shí),我們希望能有地方寫回復(fù),可以提交,可以取消,由于這幾個(gè)元素都是后來(lái)添加的,所以我們將行為都托管到評(píng)論列表這個(gè)元素。

下面先將提交評(píng)論事件函數(shù)。

addCmt 函數(shù)

fn.addCmt = function (_btn, _text, _parent) { //防止多次點(diǎn)擊 if(_btn.attr('data-disabled') == 'true') {  return !1; } //處理提交空白 var value = _text.val().replace(/^/s+|/s+$/g, ''); value = value.replace(/[/r/n]/g,'<br >'); if(!value){  alert('內(nèi)容不能為空');  return !1; } //禁止點(diǎn)擊 _btn.attr('data-disabled','true'); _btn.html('評(píng)論提交中...'); //提交處理 var self = this,  email, username; username = $.cookie('user'); if (!username) {  username = '游客'; } email = $.cookie('email'); if (!email) {  email = 'default@163.com'; } var now = new Date(); $.ajax({  type: 'get',  dataType: 'json',  url: this.setCmtUrl,  data: {   belong: self.belong,   parent: _parent,   email: email,   username: username,   content: value  },  success: function(_data){   //解除禁止點(diǎn)擊   _btn.attr('data-disabled', '');   _btn.html('提交評(píng)論');   if (!_data) {    alert('評(píng)論失敗,請(qǐng)重新評(píng)論');    return !1;   }   if (_data['result'] == 1) {    //評(píng)論成功    alert('評(píng)論成功');    var id = _data['id'],     time = now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate() + ' ' +      now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();    if (_parent == 0) {     var index = self.lists.length;     if (!self.pager) {      //設(shè)置分頁(yè)器      self.noCmt.css('display', 'none');      var total = Math.ceil(self.lists.length / self.offset);      self.pager = new Pager({       index: 1,       total: total,       parent: self.pagerBox[0],       onchange: self.doChangePage.bind(self),       label:{        prev: '<',        next: '>'       }      });     }     self.keys[id] = index;     self.lists.push({      "id": id,      "username": username,      "time": time,      "content": value,      "response": []     });     self.showList(1);     self.pager._$setIndex(1);     }else {     var index = self.keys[_parent],      page = self.pager.__index;     self.lists[index]['response'].push({      "id": id,      "username": username,      "time": time,      "content": value     });     self.showList(page);    }    self.text.val('');   } else {    alert('評(píng)論失敗,請(qǐng)重新評(píng)論');   }  },  error: function () {   alert('評(píng)論失敗,請(qǐng)重新評(píng)論');   //解除禁止點(diǎn)擊   _btn.attr('data-disabled', '');   _btn.html('提交評(píng)論');  } });}

參數(shù)有3個(gè):_btn, _text, _parent 之所以要有這三個(gè)參數(shù)是因?yàn)樵u(píng)論或者回復(fù)這樣才能使用同一個(gè)函數(shù),從而不用分開寫。

點(diǎn)擊后就是常見的防止多次提交,檢查一下cookie中有沒(méi)有username、email等用戶信息,沒(méi)有就使用游客身份,然后處理一下內(nèi)容,去去掉空白啊,/n換成 <br> 等等,檢驗(yàn)過(guò)后發(fā)起ajax請(qǐng)求。

成功后把新的評(píng)論放到this.lists,然后執(zhí)行this.showList(1)刷新顯示

php部分仍然不講,sql語(yǔ)句如下:

$parent = $_GET['parent'];$belong = $_GET['belong'];$content = htmlentities($_GET['content']);$username = $_GET['username'];$email = $_GET['email'];$query = "insert into comments (parent,belong,content,time,username,email) value ($parent,$belong,'$content',NOW(),'$username','$email')";doClickResponse 函數(shù)fn.doClickResponse = function(_event){ var target = $(_event.target); var id = target.attr('data-id'); if (target.hasClass('response') && target.attr('data-disabled') != 'true') {  //點(diǎn)擊回復(fù)  var oDiv = document.createElement('div');  oDiv.className = 'cmt-form';  oDiv.innerHTML = '<textarea class="cmt-text" placeholder="歡迎建議,提問(wèn)題,共同學(xué)習(xí)!"></textarea>' +   '<button class="u-button resBtn" data-id="' + id + '">提交評(píng)論</button>' +   '<a href="javascript:void(0);" class="cancel">[取消回復(fù)]</a>';  target.parent().parent().append(oDiv);  oDiv = null;  target.attr('data-disabled', 'true'); } else if (target.hasClass('cancel')) {  //點(diǎn)擊取消回復(fù)  var ppNode = target.parent().parent(),   oRes = ppNode.find('.response').eq(0);  target.parent().remove();  oRes.attr('data-disabled', ''); } else if (target.hasClass('resBtn')) {  //點(diǎn)擊評(píng)論  var oText = target.parent().find('.cmt-text').eq(0),   parent = target.attr('data-id');  this.addCmt(target, oText, parent); }else{  //其他情況  return !1; }};

根據(jù)target.class來(lái)判斷點(diǎn)擊的是哪個(gè)按鈕。

如果點(diǎn)擊回復(fù),生成html,放到這條評(píng)論的后面

var oDiv = document.createElement('div');oDiv.className = 'cmt-form';oDiv.innerHTML = '<textarea class="cmt-text" placeholder="歡迎建議,提問(wèn)題,共同學(xué)習(xí)!"></textarea>' +     '<button class="u-button resBtn" data-id="' + id + '">提交評(píng)論</button>' +     '<a href="javascript:void(0);" class="cancel">[取消回復(fù)]</a>';target.parent().parent().append(oDiv);oDiv = null;target.attr('data-disabled', 'true'); //阻止重復(fù)生成html

點(diǎn)擊取消,就把剛才生成的remove掉

var ppNode = target.parent().parent(), oRes = ppNode.find('.response').eq(0);target.parent().remove();oRes.attr('data-disabled', ''); //讓回復(fù)按鈕重新可以點(diǎn)擊

點(diǎn)擊提交,獲取一下該獲取的元素,直接調(diào)用addCmt函數(shù)

var oText = target.parent().find('.cmt-text').eq(0), parent = target.attr('data-id');this.addCmt(target, oText, parent);

注意: parent剛才生成html時(shí)我把它存在了提交按鈕的data-id上了。

到此全部功能都實(shí)現(xiàn)了,希望對(duì)大家的學(xué)習(xí)有所啟發(fā)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 龙里县| 明星| 昆明市| 广汉市| 文水县| 蒙山县| 石城县| 钟山县| 孟连| 宜昌市| 徐水县| 云和县| 左贡县| 酉阳| 泗水县| 绵阳市| 仙游县| 洛隆县| 繁昌县| 舟曲县| 兴海县| 海南省| 赤城县| 临澧县| 通化市| 平阴县| 左贡县| 达尔| 衢州市| 宜阳县| 德江县| 宾川县| 阿勒泰市| 乌鲁木齐县| 宁陵县| 宜昌市| 淳化县| 精河县| 遂川县| 沅陵县| 江川县|