網頁 Flow 上的小幫手
平常雖然會搭配jquery,但是有一些功能卻很長花時間在寫一樣的 Code,所以決定整理起一些常用的 Function,可以快速解決一些 UI 上的問題。
判斷 UserAgent
網頁有時候需要針對不同的 瀏覽器 或是 device 做一些 UI 變化,我目前用過最好用的是 ua-parser-js。
用起來也超級簡單無痛上手,已經試過 IE8, IE9 也都正確判斷。
function detectLowerBrowser() {
var uaParser = new UAParser();
if (uaParser) {
uaBrowser = uaParser.getBrowser();
if (uaBrowser.name === 'IE') {
// show hint for IE
}
}
}
以前會這樣自己寫
//javascript 用法
navigator.userAgent;
//結果
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36"
//加上一些判斷
if(navigator.userAgent.indexOf("MSIE 8") != -1 || navigator.userAgent.indexOf("MSIE 6") != -1 || navigator.userAgent.indexOf("MSIE 7") != -1) {
//如果是IE 8,IE 6,IE 7
} else {
//如果是其他的
}
或是用 PHP 去判斷 User Agent。
<?php
$useragent=$_SERVER['HTTP_USER_AGENT'];
$deviceType = 'unknow';
$osType = 'unknow';
$browserType = 'unknow';
$androidVersion = 'unknow';
$iosVersion = 'unknow';
$appleWebKitVersion = 'unknow';
if(preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i',$useragent)||preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i',substr($useragent,0,4))){
$deviceType = 'mobile';
} else {
$deviceType = 'desktop';
}
if (strpos($useragent, 'iPad') !== false || strpos($useragent, 'GT-N8000') !== false || strpos($useragent, 'Android') !== false || strpos($useragent, 'iPhone') !== false || strpos($useragent, 'iPad') !== false ) {
// ipad 很怪的被分為 desktop;
// samsung galaxy note 10.1 也是;
$deviceType = 'mobile';
}
if ( $deviceType == 'mobile'){
if(strpos($useragent, 'iPhone') !== false || strpos($useragent, 'iPad') !== false){
$osType = 'ios';
//取得是不是 ios 7.0 以上;
$iosString = '/(\d+)_(\d+)_(\d+) like Mac/';
$iosMatch = preg_match($iosString, $useragent, $match);
if( $iosMatch ){
if($match[1] > 6){
$iosVersion ='yes';
} else {
$iosVersion ='no';
}
}
} else if (strpos($useragent, 'Android')!== false){
$osType = 'android';
//取得是不是 Android 4.0 以上;
$androidString = '/Android\s(\d+)\.(\d+)/';
$androidMatch = preg_match($androidString, $useragent, $match2);
if( $androidMatch ){
if($match2[1] >= 4){
$androidVersion ='yes';
} else {
$androidVersion ='no';
}
}
} else {
$osType = 'other';
}
if(strpos($useragent, 'Chrome') !== false || strpos($useragent, 'CriOS') !== false){
$browserType = 'Chrome';
} else if ( strpos($useragent, 'Safari') !== false){
$browserType = 'Safari';
} else {
$browserType = 'other';
}
} else if ($deviceType == 'desktop'){
if(strpos($useragent, 'Mac OS X') !== false ){
$osType = 'Mac';
} else if (strpos($useragent, 'Windows') !== false ){
$osType = 'Windows';
} else {
$osType = 'other';
}
if(strpos($useragent, 'Chrome') !== false ){
$browserType = 'Chrome';
} else {
$browserType = 'notChrome';
}
if(strpos($useragent, 'MSIE') !== false ){
header('Location: ie-message.php');
}
}
if($browserType == 'Chrome'){
$webkitString = '/AppleWebKit\/(\d+)/';
$webkitMatch = preg_match($webkitString, $useragent, $match3);
if( $webkitMatch ){
if($match3[1] >= 537){
$appleWebKitVersion ='yes';
} else {
$appleWebKitVersion ='no';
}
}
}
?>
<script type="text/javascript"></script>
jquery Index 連不一樣的東西都算進去
<div class="hellopwrap">
<p class="hellop"></p>
<p class="hellop"></p>
<h1>Yo</h1>
<p class="hellop"></p>
<p class="hellop"></p>
<p class="hellop"></p>
<p class="hellop"></p>
</div>
$('.hellop').each(function(){
var thisIndex = $(this).closest('.hellopwrap').find('.hellop').index(this);
});
scrollToEle
網頁很常用的滑到某個 Section 功能,有時候想要動畫,有時候不想要,有時候想要滑到該物件在網上一段距離,不要貼邊。這些繁瑣的事情都可以一次搞定。
注意!因為 IE 跟 Firfox 不支援 body
scrollTop,所以務必要 html,body都寫。
//範例
scrollToEle($("#happyzone"),true);
scrollToEle($("#happyzone"),false);
scrollToEle($("#happyzone"),false,1500);
function scrollToEle(element,animate,posY){
if(element.length > 0){
if(!posY){
//自己的位置;
posY = element.offset().top-20;
} else { //指定的位置;
}
if(animate){
$('html, body').animate({"scrollTop":posY},200);
} else {
$('html, body').scrollTop(posY);
}
} else {
return false;
}
}
overflowBody
網頁剛進來在 Loading 的時候你可能不想要先看到捲軸,想要 Loading 畫面 FadeOut 之後才出現右方捲軸,此時可以派上用場。
//範例
overflowBody(true);
overflowBody(false);
function overflowBody(status){
if(status){
$('body').addClass('freeze');
} else {
$('body').removeClass('freeze');
}
}
body {
...
&.freeze {
position: fixed;
top: 0;
left: 0;
}
}
以前是用下面這樣的寫法,但發現 hidden 之後再解開,會常常不能恢復捲動
function overflowBody(status){
winW = $(window).width();
winH = $(window).height();
if(status){
$('body').css({
'height':winH,
'width':winW,
'overflow':'hidden'
});
} else {
$('body').css({
'height':'auto',
'width':'100%',
'overflow':'auto'
});
}
}
loadExtraFile
有一些檔案你可能是按下某個按鈕,或是切換某個選項才要導入某個 CSS 或是 JS 檔,所以不能放在 head 也不能放在某一頁。
//範例
loadExtraFile("theme/JP.css", "css",function(){console.log("done");});
loadExtraFile("lang/EN.js", "js",function(){console.log("done");});
function loadExtraFile(filename, filetype, callback){
var thisCallback = callback;
var timeStamp = createTimeStamp();
filename = filename + '?time='+timeStamp;
if (filetype == "js"){ //if filename is a external JavaScript file
var fileref = document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filename);
}
else if (filetype == "css"){ //if filename is an external CSS file
var fileref = document.createElement("link");
fileref.setAttribute("rel", "stylesheet");
fileref.setAttribute("type", "text/css");
fileref.setAttribute("href", filename);
}
if (typeof fileref != "undefined"){
document.getElementsByTagName("head")[0].appendChild(fileref);
var load_done = false;
fileref.onload = fileref.onreadystatechange = function() {
if ( !load_done && (!this.readyState ||
this.readyState === "loaded" || this.readyState === "complete") ) {
load_done = true;
if(thisCallback && typeof(thisCallback) == "function"){
thisCallback();
}
}
};
}
}
svdebug
IE8有 console.log 會掛掉,所以上線後會很難 Debug,此時可以再上線前將所有 console.log()
換成自己寫的 log 工具 ex : svdebug()
。
//打開console
sysDebug = true;
//接下你做的每個動作就有 console 了。
var sysDebug = false;
function svdebug(string){
if(sysDebug == true){
console.debug(string);
}
}
離開網頁偵測 setLeavePageJS
這邊其實本來很簡單,但因為 iOS 叫做 pagehide
不叫 beforeunload
所以只好自己客製化判斷。
function setLeavePageJS(){
var eventName = isIOS ? "pagehide" : "beforeunload";
$(window).on(eventName, function(e) {
var e = e || window.event;
var theStr = "";
//你也可以 String 都一樣我只是要示範說可以不一樣。
if(eventName == "beforeunload"){
var theStr = "確定要離開網頁嗎?";
} else if (eventName == "pagehide"){
var theStr = "確定要離開 iOS 的網頁嗎?";
}
if(theStr != ""){ return theStr;}
});
}
偵測 iframe post 的 Message
這比較常用在登入的 iframe 之類的地方,例如你利用 iframe 登入,成功後外面的網頁要 reload.
//當登入成功後 iframe 裡的 js 呼叫
window.parent.postMessage('login_reload', '*');
// iframe 外層的 parent 就會接收到 message
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event){
if (event.origin !== serverhost){
return;
}else{
if(event.data=='login_reload'){
window.top.location.reload();
}
}
}
animate height From ? to auto
這個在 Q & A 的 Layout 非常常用到。
$('.qa-expand').on('click',function(){
var qa = $(this).closet('.qa-wrap');
var curHeight = qa.height();
var autoHeight = qa.css('height', 'auto').height();
qa.height(curHeight).animate({height: autoHeight}, 1000);
});
每 N 個一樣的東西,包一個 DIV
var totalAD = $('.ad');
for(var i = 0; i < totalAD.length; i+=4) {
totalAD.slice(i, i+4).wrapAll('<div class="ad-row center-1000"></div>');
}
searchInDOM
這個會用在你的網頁有一堆 Grid、Card、List,但是數量不多,你不想透過資料庫查詢,只想要把使用者想找的顯示其他隱藏,可以搭配一個 search input 使用。
//範例
$('.search-input').on('keyup',function(){
qty = $(this).val();
searchInDOM('.cards-wrapper .cards',qty);
});
function searchInDOM(element,findSTR){
var impactContent = $(element);
var findSTRArr = findSTR.split(" ");
if(impactContent.length > 0){
impactContent.each(function(){
var string = $(this).text().toLowerCase().replace(/([\n|\t])/g, '');
var isSearched = true;
$.each(findSTRArr,function(kk,vv){
var findSTRTEXT = vv.toLowerCase();
if(string.search(findSTRTEXT) != -1){
} else {
isSearched = false;
}
});
if(isSearched){
$(this).addClass('selectedITEM');
} else {
$(this).addClass('selectedNONE');
}
});
}
}
Click to Fullscreen
$('.fullscreen-btn').on('click',function(){
var elem = document.getElementById("content");
$(elem).addClass('fullscreenMode');
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
}
});
function toggleFullScreen() {
if ((document.fullScreenElement && document.fullScreenElement !== null) ||
(!document.mozFullScreen && !document.webkitIsFullScreen)) {
if (document.documentElement.requestFullScreen) {
document.documentElement.requestFullScreen();
} else if (document.documentElement.mozRequestFullScreen) {
document.documentElement.mozRequestFullScreen();
} else if (document.documentElement.webkitRequestFullScreen) {
document.documentElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
if (document.cancelFullScreen) {
document.cancelFullScreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
}
}
}
偵測裝置轉向
window.onorientationchange = function(){
var orientation = window.orientation;
if (orientation === 0){
// is in Portrait mode.
}
else if (orientation === 90){
// is in Landscape mode. The screen is turned to the left.
}
else if (orientation === -90){
// is in Landscape mode. The screen is turned to the right.
}
}
Delay to addClass
$('.target').delay(1000).queue(function(next){
$(this).removeClass('red');
next();
});
偵測網頁現在有沒有捲軸
hasScrollBar = document.body.scrollHeight > document.documentElement.clientHeight;
偵測網頁是不是捲到最下面了
利用一些 setTimeout 跟 scrollDetect
變數減少計算的次數
var hasScrollBar = false;
var scrollDetect = true;
function toggleScrollHint() {
if (scrollDetect) {
scrollDetect = false;
scrollY = $(window).scrollTop();
var isScrollToBottom = ($(window).scrollTop() + $(window).height()) >= ($(document).height() - 120);
if (hasScrollBar && !isScrollToBottom) {
$('.scroll-to-bottom').removeClass('_hide');
setTimeout(function(){
$('.scroll-to-bottom').addClass('_show');
setTimeout(function(){
scrollDetect = true;
}, 40);
}, 40);
} else {
$('.scroll-to-bottom').removeClass('_show');
setTimeout(function(){
$('.scroll-to-bottom').addClass('_hide');
scrollDetect = true;
}, 400);
}
}
}
$(window).scroll(function () {
setTimeout(function(){
toggleScrollHint();
}, 300);
});