從13年開始我迷上了podcast辟拷,尤其是在夜晚一片寂靜的時(shí)候只磷,邊聽podcast邊寫代碼或是看書學(xué)習(xí)真是一種難以言喻的幸福庆杜。最早的時(shí)候是去新浪播客收聽的简软,可那蛋疼的速度和糟糕的設(shè)計(jì)蛮拔,讓我很快就轉(zhuǎn)向荔枝FM了。荔枝的速度在法國挺快痹升,中文播客也比較全建炫。我平時(shí)收聽的主要渠道是web端,荔枝的設(shè)計(jì)雖然簡單疼蛾,但也到清爽干凈肛跌,比起它日漸復(fù)雜的移動(dòng)端更得我心。但是荔枝FM主打的畢竟是移動(dòng)端察郁,web端一直都非常簡陋衍慎。最讓我不爽的是web端至今都沒能實(shí)現(xiàn)訂閱自選電臺這個(gè)最簡單,也最核心的功能皮钠。
既然官方等不到了稳捆,那就自己來弄一個(gè)吧。上一篇介紹過Greasemonkey和類似查件的作用了鳞芙,他們可以在訪問指定頁面的時(shí)候執(zhí)行預(yù)先寫好的腳本眷柔,以此實(shí)現(xiàn)自定義頁面。在這里原朝,我需要的是一個(gè)簡單的功能:在web端的左邊菜單欄中增加一個(gè)按鈕驯嘱,用來展開自選電臺列表。
有了功能需要喳坠,讓我們考慮一下實(shí)現(xiàn)思路吧鞠评。Dev tools和Inspect element是我們手中的利器, 很快我就發(fā)現(xiàn)一個(gè)有趣的事實(shí):該網(wǎng)站是基于angular實(shí)現(xiàn)的壕鹉!它帶有SPA的特點(diǎn)剃幌,當(dāng)我們點(diǎn)擊左邊菜單欄按鈕的時(shí)候,只有中間的內(nèi)容區(qū)會刷新晾浴。左邊的菜單欄和右邊的播放器是基本不刷新的负乡。這對我們要實(shí)現(xiàn)的功能來說真是太棒了!自選電臺的腳本只在頁面第一次加載的時(shí)候注入DOM脊凰,每次更換電臺的時(shí)候只有中間的電臺信息部分刷新抖棘,該腳本不會再次執(zhí)行,開銷小多了!
好了切省,那就開始動(dòng)手吧最岗。首先自然是怎么確定自選電臺了;觀察幾次就會發(fā)現(xiàn)朝捆,每個(gè)電臺都由唯一的數(shù)字代表般渡,只要將這個(gè)數(shù)字加到網(wǎng)址后面就可以跳轉(zhuǎn)到該電臺了。比如Gadio的地址是http://www.lizhi.fm/#/29345芙盘,友的聊的地址是http://www.lizhi.fm/#/14393/驯用。這就簡單多了,只要保存每個(gè)電臺的唯一地址就可以了:
var myRadioList = {
gadio: '[http://www.lizhi.fm/#/29345'](http://www.lizhi.fm/#/29345'),
友的聊: '[http://www.lizhi.fm/#/14393'](http://www.lizhi.fm/#/14393'),
二次元: '[http://www.lizhi.fm/#/22557'](http://www.lizhi.fm/#/22557'),
糖蒜廣播: '[http://www.lizhi.fm/#/13461'](http://www.lizhi.fm/#/13461')
};
好吧何陆,這種定義方式實(shí)在太不靈活了晨汹。不過作為第一版的腳本豹储,從簡單的贷盲,可實(shí)現(xiàn)的方案開始并無不可,日后再迭代就是了剥扣。電臺列表有了巩剖,下面就該在左邊加個(gè)按鈕了。用inspect element找到左邊導(dǎo)航欄的位置:
var ul = document.querySelector('.wrap > .leftNav > .content > ul');
然后就該生成一個(gè)按鈕加到ul上了:
var radioButton = document.createElement('button');
radioButton.class = 'my-radio';
radioButton.style.cssText = "margin:3px 3px 3px 15px";
var b = document.createElement('b');
b.innerHTML = '自選電臺'; radioButton.appendChild(b);
ul.appendChild(radioButton);
我知道這種inline css很丑钠怯,但作為一個(gè)簡單小腳本佳魔,你們就原諒我的放蕩不羈吧……
好了,按鈕有了晦炊。下面該生成列表鞠鲜,并在按鈕上綁定點(diǎn)擊事件來展開列表了。綁定事件很簡單:
radioButton.onclick = toggleRadioList;
toggleRadioList就是響應(yīng)點(diǎn)擊事件的方法断国,由該方法來展開和收起列表贤姆。得先有列表才行啊,鑒于列表本質(zhì)上就是ul元素稳衬,我們先創(chuàng)建一個(gè)ul節(jié)點(diǎn):
var radioList = document.createElement('ul');
radioList.className = 'radio-list';
radioList.style.cssText = "list-style-type:none;margin:3px;padding:0px 15px;visibility:hidden";
為了方便toggle函數(shù)改變它的狀態(tài)霞捡,特地加上了一個(gè)class來方便查找。好了薄疚,下面就該按定義的電臺對象一次生成內(nèi)部的li節(jié)點(diǎn)了:
var frag = document.createDocumentFragment();
Object.keys(itemList).forEach(function(key) {
var li = document.createElement('li');
var a = document.createElement('a');
a.href = itemList[key];
a.innerHTML = key;
a.style.cssText = "text-decoration:underline";
li.appendChild(a); frag.appendChild(li);
});
radioList.appendChild(frag);
target.appendChild(radioList);
注意這里我用了一個(gè)fragment來添加li節(jié)點(diǎn)碧信。其實(shí)這里并不是必須的,但從練習(xí)的角度來看街夭,這么寫是符合性能實(shí)踐的砰碴。我們都知道DOM操作開銷是非常高的,每次DOM變動(dòng)都會使瀏覽器重新計(jì)生成DOM樹板丽,然后重新渲染DOM呈枉。從性能的角度來看,DOM操作的次數(shù)越少越好。于是先用一個(gè)fragment來添加li節(jié)點(diǎn)碴卧,最后再將該fragment注入DOM中弱卡。這樣我們只用一次DOM操作就注入了所有的li節(jié)點(diǎn),比每生成一個(gè)li就注入要高效的多住册,隨著列表的增長婶博,性能方面的差距會越來越大。但我們這里的radioList直到最后才加入DOM荧飞,所以問題要小一些凡人,但無論如何,記得這一點(diǎn)對以后會有幫助的叹阔。
最后只剩下響應(yīng)點(diǎn)擊事件的toggleRadioList函數(shù)了挠轴。這個(gè)函數(shù)的作用就是找到class為radio-list的列表,如果該列表已經(jīng)可見了就隱藏它耳幢,否則就顯示它:
function toggleRadioList() {
var list = document.querySelector('.radio-list');
if (list.style.visibility === 'hidden')
list.style.visibility = 'visible';
else
list.style.visibility = 'hidden';
}
好了岸晦,零件都齊活了,腳本應(yīng)該可以用了吧睛藻?等等启上,我最近剛想起來一個(gè)問題:全局命名空間的沖突。大家都直到上個(gè)leanpub的腳本店印,所有函數(shù)是直接暴露在global下的冈在。這會有什么問題呢?可能會出現(xiàn)變量沖突按摘!比如我這有一個(gè)toggleRadioList變量包券,如果荔枝網(wǎng)站本身有一個(gè)同名變量,沖突就出現(xiàn)了炫贤,荔枝網(wǎng)站的某部分功能也就會出現(xiàn)問題了溅固。為了確保沖突不出現(xiàn),我們需要將我們自己定義的腳本限制在一個(gè)命名空間內(nèi)照激,不要直接暴露出來发魄。最適合完成這個(gè)任務(wù)的自然就是IIF了,詳情我在去年的一篇文卓中已有講解俩垃,就不再詳述了励幼。簡單來說,就是將我們所有的腳本都包含在這樣一個(gè)函數(shù)中:
(function() {
//自定義腳本內(nèi)容
})()
基于JS函數(shù)作用域定義口柳,里面包含的所有變量和函數(shù)定義都只在該函數(shù)內(nèi)部可見苹粟,實(shí)現(xiàn)了和global的隔離。
到這里跃闹,腳本就差不多了嵌削。還有很多可以改進(jìn)的地方毛好,比如可以增加一個(gè)輸入框讓用戶自己輸入電臺地址,然后保存到localStorage中苛秕;改進(jìn)一下CSS讓電臺列表的顯示更清晰肌访。這些東西以后再慢慢來吧,已經(jīng)快要午夜了艇劫,讓我們選一個(gè)podcast吼驶,從陌生人的聲音中取得一絲慰藉吧。
P.S: 完整腳本地址