如何用jquery實現(xiàn)留言框設(shè)計
自從React/Vue等框架流行之后,jQuery被打上了面條式代碼的標(biāo)簽,甚至成了“過街老鼠”,好像誰還在用jQuery,誰就還活在舊時代,很多人都爭先恐后地?fù)肀驴蚣?,各大博客網(wǎng)站有很大一部分的博客都在介紹新的框架,爭當(dāng)時代的“弄潮兒”。新框架帶來的新的理念,新的開發(fā)方式不可否認(rèn)帶來了生產(chǎn)效率,但是jQuery等就應(yīng)該被打上“舊時代”面條式代碼的標(biāo)簽么?
我們從一篇文章說起:《React.js 的介紹 – 針對了解 jQuery 的工程師(譯)》,英文原文是這個《React.js Introduction For People Who Know Just Enough jQuery To Get By》,這篇文章我好久前就看過,現(xiàn)在再把它翻出來,里面對比了下jQuery和React分別實現(xiàn)一個發(fā)推的功能,作者用jQuery寫著寫著代碼就亂套了,而用React不管需求多復(fù)雜,代碼條理依舊很清晰。
我們一步步按照原文作者的思路來拆解。
(1)輸入個數(shù)為0時,發(fā)送按鈕不可點(diǎn)擊
如下圖所示,當(dāng)輸入框沒有內(nèi)容時,發(fā)推按鈕置灰不可點(diǎn),有內(nèi)容點(diǎn)才能點(diǎn)。
- // 初始化狀態(tài) $("button").prop("disabled", true); // 文本框的值發(fā)生變化時 $("textarea").on("input", function() { // 只要超過一個字符,就 if ($(this).val().length > 0) { // 按鈕可以點(diǎn)擊 $("button").prop("disabled", false); } else { //否則,按鈕不能點(diǎn)擊 $("button").prop("disabled", true); } });
 
這個代碼本身寫得很累贅,首先,既然一開始那個button是disabled的,那就直接在html上寫個disabled屬性就行了:
- <form class="tweet-box">
 - <textarea name="textMsg"></textarea>
 - <input disabled type="submit" name="tweet" value="Tweet">
 - </form>
 
第二個要控制按鈕的狀態(tài),其實核心只要一行代碼就行了,不需要寫那么長:
- let form = $(".tweet-box")[0];
 - $(form.textMsg).on("input", function() {
 - form.tweet.disabled = this.value.length <= 0;
 - }).trigger("input");
 
這個代碼應(yīng)該夠簡潔了吧,而且代碼在jQuery和原生之間來回切換,游刃有余。
(2)實現(xiàn)剩余字?jǐn)?shù)功能
如下圖所示:
- let form = $(".tweet-box")[0], $leftWordCount = $("#left-word-count"); $(form.textMsg).on("input", function() { // 已有字?jǐn)?shù) let wordsCount = this.value.length; $leftWordCount.text(140 - wordsCount); form.tweet.disabled = wordsCount <= 0; });
 
(3)添加圖片按鈕
如下圖所示,左下角多了一個選擇照片的按鈕:
如果用戶選擇了照片,那么可輸入字?jǐn)?shù)將會減少23個字符,并且Add Photo文案要變成Photo Added。我們先來看下作者是怎么實現(xiàn)的,如下代碼:
- if ($(this).hasClass("is-on")) { $(this) .removeClass("is-on") .text("Add Photo"); $("span").text(140 - $("textarea").val().length); } else { $(this) .addClass("is-on") .text("✓ Photo Added"); $("span").text(140 - 23 - $("textarea").val().length); }
 
如果代碼像作者這樣寫的話確實是比較亂,而且比較面條式。但是我們可以優(yōu)雅地實現(xiàn)。首先,選擇照片一般會寫一個input[type=file]的隱藏輸入框蓋在上傳圖標(biāo)下面:
- <div class="upload-container">
 - <img src="upload-icon.png" alt>
 - <span id="add-photo">Add Photo</span>
 - <input type="file" name="photoUpload">
 - </div>
 
然后監(jiān)聽它的change事件,在change事件里面給form套一個類:
- $(form.photoUpload).on("change", function() {
 - // 如果選擇了照片則添加一個photo-added的類
 - this.value.length ? $(form).addClass("photo-added")
 - // 否則去掉
 - : $(form).removeClass("photo-added");
 - });
 
然后就可以來實現(xiàn)文案改變的需求了,把上面#add-photo的span標(biāo)簽添加兩個data屬性,分別是照片添加和未添加的文案,如下代碼所示:
- <span id="add-photo" data-added-text="Photo Added"
 - data-notadded-text="Add Photo">
 
- </span>
 
通過form的類結(jié)合before/after偽類控制html上的文案,如下代碼所示:
- #add-photo:before {
 - content: attr(data-empty-text);
 - }
 - form.photo-added #add-photo:before {
 - content: attr("data-added-text);
 - }
 
這樣就可以了,我們算是用了一個比較優(yōu)雅的方式實現(xiàn)了一個文案變化的功能,其中CSS的attr可以兼容到IE9,并且這里html/css/js相配合,
共同完成這個變化的功能,這應(yīng)該也挺好玩的。
剩下一個要減掉23字符的需求,只需要在減掉的時候判斷一下:
- $(form.textMsg).on("input", function() {
 - // 已有字?jǐn)?shù)
 - let wordsCount = this.value.length;
 - form.tweet.disabled = wordsCount <= 0;
 - $leftWordCount.text(140 - wordsCount -
 - //如果已經(jīng)添加了圖片再減掉23個字符
 - ($(form).hasClass("photo-added") ? 23 : 0));
 - });
 
然后在選擇圖片之后trigger一下,讓文字發(fā)生變化,如下代碼倒數(shù)第二行:
- /*
 - * @trigger 會觸發(fā)文字輸入框的input事件以更新剩余字?jǐn)?shù)
 - */
 - $(form.photoUpload).on("change", function() {
 - // 如果選擇了照片則添加一個photo-added的類
 - this.value.length ? $(form).addClass("photo-added") :
 - // 否則去掉
 - $(form).removeClass("photo-added");
 - $(form.textMsg).trigger("input");
 - });
 
這里又使用了事件的機(jī)制,用reac應(yīng)該基本上都是用狀態(tài)state控制了。再來看***一個功能。
(4)沒有文字但是有照片發(fā)推按鈕要可點(diǎn)
上面是只要沒有文字,那么發(fā)推按鈕不可點(diǎn),現(xiàn)在要求有圖片就可點(diǎn)。這個也好辦,因為如果有圖片的話,form已經(jīng)有了一個類,所以只要再加一個判斷就可以了:
- $(form.textMsg).on("input", function() {
 - // 已有字?jǐn)?shù)
 - let wordsCount = this.value.length;
 - form.tweet.disabled = wordsCount <= 0
 - //disabled再添加一個與判斷
 - && !$(form).hasClass("photo-added");
 - $leftWordCount.text(140 - wordsCount -
 - //如果已經(jīng)添加了圖片再減掉23個字符
 - ($(form).hasClass("photo-added") ? 23 : 0));
 - });
 
***看一下,匯總的JS代碼,加上空行和注釋總共只有23行:
- let form = $(".tweet-box")[0],
 - $leftWordCount = $("#left-word-count");
 - $(form.textMsg).on("input", function() {
 - // 已有字?jǐn)?shù)
 - let wordsCount = this.value.length;
 - form.tweet.disabled = wordsCount <= 0
 - //disabled再添加一個與判斷
 - && !$(form).hasClass("photo-added");
 - $leftWordCount.text(140 - wordsCount -
 - //如果已經(jīng)添加了圖片再減掉23個字符
 - ($(form).hasClass("photo-added") ? 23 : 0));
 - });
 - /*
 - * @trigger 會觸發(fā)文字輸入框的input事件以更新剩余字?jǐn)?shù)
 - */
 - $(form.photoUpload).on("change", function() {
 - // 如果選擇了照片則添加一個photo-added的類
 - this.value.length ? $(form).addClass("photo-added") :
 - // 否則去掉
 - $(form).removeClass("photo-added");
 - $(form.textMsg).trigger("input");
 - });
 
html大概有10行,還有6行核心CSS,不過這兩個比較易讀。再來看一下React的完整版本,作者的實現(xiàn):
- var TweetBox = React.createClass({
 - getInitialState: function() {
 - return {
 - text: "",
 - photoAdded: false
 - };
 - },
 - handleChange: function(event) {
 - this.setState({ text: event.target.value });
 - },
 - togglePhoto: function(event) {
 - this.setState({ photoAdded: !this.state.photoAdded });
 - },
 - remainingCharacters: function() {
 - if (this.state.photoAdded) {
 - return 140 - 23 - this.state.text.length;
 - } else {
 - return 140 - this.state.text.length;
 - }
 - },
 - render: function() {
 - return (
 - <div className="well clearfix">
 - <textarea className="form-control"
 - onChange={this.handleChange}></textarea>
 - <br/>
 - <span>{ this.remainingCharacters() }</span>
 - <button className="btn btn-primary pull-right"
 - disabled={this.state.text.length === 0 && !this.state.photoAdded}>Tweet</button>
 - <button className="btn btn-default pull-right"
 - onClick={this.togglePhoto}>
 - {this.state.photoAdded ? "✓ Photo Added" : "Add Photo" }
 - </button>
 - </div>
 - );
 - }
 - });
 - React.render(
 - <TweetBox />,
 - document.body
 - );
 
React的套路是監(jiān)聽事件然后改變state,在jsx的模板里,使用這些state展示,而jQuery的套路是監(jiān)聽事件,然后自己去控制DOM展示。
React幫你操作DOM,jQuery要自己去操作DOM,前者提供了便利但同時也失去了靈活性,后者增加了靈活性但同時增加了復(fù)雜度。
使用jQuery不少人容易寫出面條式的代碼,但是寫代碼的風(fēng)格我覺得和框架沒關(guān)系,關(guān)鍵還在于你的編碼素質(zhì),就像你用了React寫class,你就可以說你就是面向?qū)ο罅?不見得,我在《JS與面向?qū)ο蟆愤@篇文章提到,寫class并不代表你就是面向?qū)ο螅嫦驅(qū)ο笫且环N思想而不是你代碼的組織形式。一旦你離開了React的框架,是不是又要回到面條式代碼的風(fēng)格了?如果是的話那就說明你并沒有沒有掌握面向?qū)ο蟮乃枷?。不過,React等框架能夠方便地組件化,這點(diǎn)是不可否認(rèn)的。
還有一個需要注意的是,框架會幫你屏蔽掉很多原生的細(xì)節(jié),讓你專心于業(yè)務(wù)邏輯,但往往也讓你喪失了原生的能力不管是html還是js,而這才是最重要的功底。例如說對于事件,由于所有的事件都是直接綁在目標(biāo)元素,然后通過state或者其它第三方的框架進(jìn)行傳遞,這樣其實就沒什么事件的概念了。所以需要警惕使用了框架但是喪失了基本的前端能力,再如ajax分頁改變url,或者說單頁面路由的實現(xiàn)方式,還有前后退的控制,基本上能夠完整回答地比較少。很多人都會用框架做頁面,但是不懂JS.
【本文為51CTO專欄作者“太平洋警察”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者獲取授權(quán)】



















 
 
 





 
 
 
 