錯誤頻率較高的JS&CSS問題
勤能補拙,不管是哪門子技術,在實踐中多多總結,開發效率慢慢就會提升。本篇介紹幾個經常出錯的JS&CSS問題,包括事件冒泡、(使用offset、scroll、clientHeight定位元素)、模仿JD寫個右欄菜單、元素水平和垂直居中、inline元素遇見padding和margin、圓角兼容性處理。這些問題自己都是寫代碼實驗過,所以都會結合Demo一起分析。相信大家也比較容易理解。另外,自己也是小菜一個,有什么解釋有誤的地方,請大神多多指定。
事件冒泡
DOM的事件冒泡機制和WPF很相似,DOM事件機制包含冒泡和捕獲兩種,按照topmost element->innermost element方向傳遞事件被稱為捕獲方式,而從innermost element->topmost element方向傳遞事件被稱為冒泡方式。
事件傳遞說明:
1.捕獲(Capture)傳遞方式通過:1>2>3。
2.冒泡(Bubble) 傳遞方式通過:3>2>1。
這里要特別說明的是,在<IE9的瀏覽器沒有捕獲機制,只有冒泡機制。基於W3C標准,事件傳遞分捕獲和冒泡兩個階段。那么我們注冊事件時怎樣注冊到不同的階段?
大家應該都熟悉addEventListener方法,在使用時很多時候都是傳遞兩個參數:
document.getElementById("d1").addEventListener("click", function(event){ alert("d1"); });
但addEventListener為我們提高了三個參數,最后一個是bool值,默認是false。正是通過這個參數來區分捕獲和冒泡方式。false表示冒泡,true表示隧道。
//冒泡方式 document.getElementById("d1").addEventListener("click", function(event){ alert("d1"); , false); //捕獲方式 document.getElementById("d1").addEventListener("click", function(event){ alert("d1"); , true);
在事件的傳遞階段,我們怎么取消事件的傳遞?event參數體提供了取消辦法。下面代碼說明了取消使用代碼:
document.getElementById("d3").addEventListener("click", function(event){ alert("d3"); event = event || window.event; //W3C標准 if(event.stopPropagation){ event.stopPropagation(); } //IE兼容 else{ event.cancelBubble = true; } }, false);
下面給出可運行的測試Demo,執行代碼可以很清楚的看到事件傳遞的兩個階段並且可以在兩個階段的任意元素上終止事件的傳遞。
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <style type="text/css"> .div-class-1, .div-class-2, .div-class-3{ padding: 30px; } .div-class-1{ width: 200px; height: 200px; background: #fd0303; } .div-class-2{ width: 100px; height: 100px; background: #fdf403; } .div-class-3{ width: 50px; height: 50px; background: #03fd38; } </style> <script type="text/javascript" src="jquery-2.2.3.min.js"></script> <script type="text/javascript"> $(function(){ document.getElementById("d1").addEventListener("click", function(event){ alert("d1"); }, false); document.getElementById("d2").addEventListener("click", function(event){ alert("d2"); }, false); document.getElementById("d3").addEventListener("click", function(event){ alert("d3"); event = event || window.event; //W3C標准 if(event.stopPropagation){ event.stopPropagation(); } //IE兼容 else{ event.cancelBubble = true; } }, false); document.getElementById("d1").addEventListener("click", function(event){ alert("d1"); }, true); document.getElementById("d2").addEventListener("click", function(event){ alert("d2"); }, true); document.getElementById("d3").addEventListener("click", function(event){ alert("d3"); }, true); }); </script> </head> <body> <div id="d1" class="div-class-1"> <span>d1</span> <div id="d2" class="div-class-2"> <span>d2</span> <div id="d3" class="div-class-3"> <span>d3</span> </div> </div> </div> </body> </html>
使用offset、scroll、clientHeight定位元素
有時候我們需要在頁面的右邊中間位置或者底部位置顯示懸浮面板,懸浮面板的定位需要使用offsetXXX、scrollXXX、clientXXX屬性。下面我們都拿Vertical方向來說明。
1.scrollHeight: 所有元素內容的高度(只讀),也就是頁面所有內容的整個高度。
2.scrollTop:當前元素所在位置之前的整個高度,可讀可寫。如果沒有滾動條,scrollTop為0。
3. clientHeight:元素的可見高度,不包含padding和border,只讀。
4.offsetHeight: 元素高度,包含了padding和border,只讀。
如果我們想在滾動條滾動的同時,讓元素始終顯示在瀏覽器頁面的中間位置,可以通過以下設置:
div.style.top = winScrolTop + (winclientHeight/2) - (divHeight/2) + "px";
說明:winScrolTop為window的scrollTop,winclientHeight為window的可見高度,divHeight為元素的高度。在使用clientHeight時需要注意兼容性問題,不同版本瀏覽器取clientHeight方式都不一樣。需要根據document.compatMode來判斷。
如果document.compatMode等於BackCompat表示關閉標准兼容模式,等於CSS1Compat表示開啟標准兼容性模式。BackCompat時瀏覽器高度等於document.body.clientHeight,CSS1Compat時瀏覽器高度等於document.documentElement.clientHeight。
下面是測試Demo代碼:
<!DOCTYPE HTML> <html lang="zh"> <head> <meta charset="gb2312"> <title>offset&scroll&clientHeight元素定位</title> <style type="text/css"> .body-class1{ position: absolute; width: 200px; height: 200px; background: #CD0000; right: 0; } .body-class2{ position: fixed; width: 200px; height: 200px; background: #CD0000; right: 0; bottom: 10px; } </style> <script type="text/javascript" src="jquery-2.2.3.min.js"></script> <script type="text/javascript"> /* * scrollHeight: 元素所有內容的高度(只讀) https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight * scrollTop:從元素之前所有內容的高度(可讀可寫), 如果沒有滾動條,則為0。 https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop * clientHeight:元素可見高度(只讀)。https://developer.mozilla.org/en-US/docs/Web/API/Element/clientHeight * offsetHeight: 元素高度(只讀), 包含pading、border */ function setVerticalLocation(){ var div = document.getElementById("d1"); //div的高度,包括border和padding var divHeight = div.offsetHeight; // document.compatMode:BackCompat和CSS1Compat。BackCompat:標准兼容模式關閉。CSS1Compat:標准兼容模式開啟。 //當document.compatMode等於BackCompat時,瀏覽器客戶區寬度是document.body.clientWidth; //當document.compatMode等於CSS1Compat時,瀏覽器客戶區寬度是document.documentElement.clientWidth。 //http://www.cnblogs.com/fullhouse/archive/2012/01/17/2324706.html var winclientHeight = /BackCompat/i.test("document.compatMode") ? document.body.clientHeight : document.documentElement.clientHeight; var winScrolTop = Math.max(document.body.scrollTop, document.documentElement.scrollTop); div.style.top = winScrolTop + (winclientHeight/2) - (divHeight/2) + "px"; } $(document).ready(function(){ var setTop = function(){ setTimeout(function(){ setVerticalLocation(); }, 100); } $(window).bind("scroll", function(event){ setTop(); }); $(window).bind("resize", function(event){ setTop(); }); }); </script> </head> <body> <div id="d1" class="body-class1"> <span>你得把我顯示在瀏覽器垂直中間位置</span> </div> <div id="d1" class="body-class2"> <span>你得把我顯示在瀏覽器垂直底部位置</span> </div> <h1>offset&scroll&clientHeight元素定位</h1> ...自己寫若干個分行,讓瀏覽器出現滾動條 <br><span>滾滾滾...</span> </body> </html>
模仿JD寫個右欄菜單
大家看到得當前文章頁面的右邊中間位置有幾個菜單,這個功能其實是仿照JD商城頁面做了一個懸浮停靠菜單。下面是右欄菜單生成代碼:
<!DOCTYPE HTML> <html lang="zh"> <head> <meta charset="gb2312"> <title>右欄菜單</title> <style type="text/css"> .h-tab{ position: fixed; width: 36px; right: 0; top: 100px; border-right: 2px solid #7a6e6e; padding: 0; margin: 0; } .h-tab .h-bar{ position: relative; background: #7a6e6e; width: 35px; height: 35px; margin-bottom: 3px; color: #fff; font-weight: bold; font-size: 10px; line-height: 35px; } .h-tab .h-bar-title{ width: 35px; height: 35px; text-align: center; background: #7a6e6e; position: absolute; z-index: 1000; text-indent: 5px; right: -1px; } .h-tab .h-bar-title:hover{ background: #c81623; cursor: pointer; } .h-tab .h-bar-desc{ position: absolute; background: #c81623; height: 35px; width: 100px; top: 0; right: -65px; z-index: 900; text-align: left; text-indent: 5px; } .hid{ display: none; } </style> <script type="text/javascript" src="jquery-2.2.3.min.js"></script> <script type="text/javascript"> function Rightmenu(){ } Rightmenu.prototype.removeAdapterMenu = function(){ $(".h-tab").find("#adapterMenu, #fastcomment, #recommend").parents(".h-bar").remove(); return this; } Rightmenu.prototype.createRightMenu = function(){ var html = '<div class="h-tab">' + '<div class="h-bar">' + '<div id="goTop" class="h-bar-title">' + '<span>↑</span>' + '</div>' + '<div class="h-bar-desc hid">' + '<span>頂部</span>' + '</div>' + '</div>' + '<div class="h-bar">' + '<div id="adapterMenu" class="h-bar-title">' + '<span>關</span>' + '</div>' + '<div class="h-bar-desc hid">' + '<span>菜單</span>' + '</div>' + '</div>' + '<div class="h-bar">' + '<div id="fastcomment" class="h-bar-title">' + '<span>注</span>' + '</div>' + '<div class="h-bar-desc hid">' + '<span>評論</span>' + '</div>' + '</div>' + '<div class="h-bar">' + '<div id="recommend" class="h-bar-title">' + '<span>我</span>' + '</div>' + '<div class="h-bar-desc hid">' + '<span>推薦</span>' + '</div>' + '</div>' + '<div class="h-bar">' + '<div id="goBottom" class="h-bar-title">' + '<span>↓</span>' + '</div>' + '<div id="" class="h-bar-desc hid">' + '<span>底部</span>' + '</div>' + '</div>' + '</div>'; $("body").append(html); return this; } Rightmenu.prototype.registEvent = function(){ $(".h-tab .h-bar").bind("mouseenter", function(){ //停止其他動畫 $(".h-tab .h-bar").find(".h-bar-desc").finish(); $(this).find(".h-bar-desc").removeClass("hid"); $(this).find(".h-bar-desc").delay(100).animate({right: "-1px"}, 400); }); $(".h-tab .h-bar").bind("mouseleave", function(){ //停止其他動畫 $(".h-tab .h-bar").find(".h-bar-desc").finish(); var desc = $(this).find(".h-bar-desc"); $(desc).animate({right: "-65px"}, 100, "linear", function(){ $(desc).addClass("hid"); }); }); //注冊go top事件 $("#goTop").bind("click", function(event){ locateElement("#header"); }); //注冊menu事件 $("#adapterMenu").bind("click", function(event){ if(!$("#footer_menu_container").hasClass("hid")){ $("#footer_menu_container").addClass("hid"); }else{ $("#footer_menu_container").removeClass("hid"); } }); //注冊fastcomment事件 $("#fastcomment").bind("click", function(event){ locateElement("#footer"); }); //注冊recommend事件 $("#recommend").bind("click", function(event){ if(!$("#heavifooter").hasClass("hid")){ $("#heavifooter").addClass("hid"); }else{ $("#heavifooter").removeClass("hid"); } }); //注冊go Bottom事件 $("#goBottom").bind("click", function(event){ locateElement("#footer"); }); return this; } $(document).ready(function(){ (new Rightmenu()).createRightMenu().registEvent().removeAdapterMenu(); }); </script> </head> <body> </body> </html>
生成元素之后,我們要讓元素始終顯示在中間位置。基本上有兩個步驟,先初始化到中間位置,然后注冊瀏覽器的resize事件,當瀏覽器大小改變時自動調節位置。下面是我寫的一段定位任何元素的代碼:
function FloatPage(id, position){
this.id = id;
this.position = position;
}
FloatPage.prototype.init = function(){
var c = this;
this.autoAdapt();
$(window).bind("resize", function(event){
c.autoAdapt();
});
}
FloatPage.prototype.autoAdapt = function(){
var element = document.getElementById(this.id);
var eHeight = element.offsetHeight;
var winHeight = /BackCompat/i.test("document.compatMode") ? document.body.clientHeight : document.documentElement.clientHeight;
if(this.position == "top"){
element.style.top = 0;
}else if(this.position == "center"){
element.style.top = (winHeight/2) - (eHeight/2) + 'px';
}else {
element.style.top = winHeight - eHeight + 'px';
}
}
下面一段代碼調用創建菜單方法,然后定位元素並且注冊resize事件。代碼如下:
//創建右欄菜單
var rightMenu = (new Rightmenu()).createRightMenu().registEvent();
//設置右欄菜單停靠位置
(new FloatPage("rightMenuTab", "center")).init();
元素水平和垂直居中
水平居中
居中分為水平和垂直居中,水平居中比較容易解決。如果是非塊級別的inline元素(例如顯示文字的span)設置水平居中,直接設置樣式:
span{ text-align: center; }
如果是塊級別的inline-block或者block元素,如果已知width的情況下可通過下面方式設置居中:
.children{ background: red; width: 50px; height: 50px; margin: 0 auto; }
垂直居中
級別為inline-block或者inline的元素,可通過設置line-height讓元素居中顯示。
垂直居中比水平居中稍微復雜些,不管是哪種方式,一般都會設置css屬性position為absolute定位元素。在已知height情況下可結合
top和margin-top設置居中:
.children{ width: 50px; height: 50px; left: 50%; margin-left: -25px; position: absolute; top: 50%; margin-top: -25px; }
說明:上面的樣式也包含了水平居中的方法。
在不知道height的情況,可以使用W3C提供的transform屬性。設置如下:
.children{ left: 50%; top: 50%; transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); /* IE 9 */ -moz-transform: translate(-50%, -50%); /* Firefox */ -webkit-transform: translate(-50%, -50%); /* Safari 和 Chrome */ -o-transform: translate(-50%, -50%); /* Opera */ }
說明:translate(-50%, -50%)表示元素的位置向左偏移元素寬度的50%,向上偏移高度的50%。完整的測試代碼如下:
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <style type="text/css"> .parent{ background: green; width: 300px; height: 200px; position: relative; } /* * 方法一,inline-block或者block元素水平居中方式 */ /*.children{ background: red; width: 50px; height: 50px; margin: 0 auto; } */ /* ** 方案二,知道元素的width和height .children{ width: 50px; height: 50px; left: 50%; margin-left: -25px; position: absolute; top: 50%; margin-top: -25px; } */ /* ** 方案三,不知道元素的width和height */ .children{ left: 50%; top: 50%; transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); /* IE 9 */ -moz-transform: translate(-50%, -50%); /* Firefox */ -webkit-transform: translate(-50%, -50%); /* Safari 和 Chrome */ -o-transform: translate(-50%, -50%); /* Opera */ } </style> <script type="text/javascript"> function submitByNA(value){ alert(value); } </script> </head> <body> <div class="parent"> <div class="children"> </div> </div> <div> </body> </html>
inline元素遇見padding和margin
padding和margin對inline-block和block元素都能生效。而對inline來說,padding和margin只對水平設置生效。測試demo如下:
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <style type="text/css"> .body-class-block{ display: block; background: yellow; /*margin: 10px; */ /*有效*/ /*padding: 10px; */ /*有效*/ } .body-class-inlineblock{ display: inline-block; background: red; /* margin: 20px 10px; */ /* 有效 */ /* padding: 20px 10px; */ /* 有效 */ } .body-class-inline{ display: inline; background: green; /*margin: 20px 10px; */ /* margin只對左右有效 */ /*padding: 20px 10px; */ /* padding只對左右有效 */ line-height: 2em; /* 如果要設置高度,可使用line-height */ } </style> </head> <body> <div class="body-class-block"> <span>block element</span> </div> <div class="body-class-inlineblock"> <span>inline-block element</span> </div> <div style="clear: both;"></div> <div class="body-class-inline"> <span>inline element</span> </div> <div class="body-class-inline"> <span>inline element</span> </div> </body> </html>
圓角兼容性處理
在使用CSS3提供的border-radius和graident屬性時,我們得考慮兼容性,IE9以下的版本都不支持這些屬性,為了解決兼容性,可以使用PIE框架,下面的這段代碼大家可以在IE9以下的IE瀏覽器下測試兼容性。但得自己到PIE官網上去下載PIE框架代碼。
<!DOCTYPE HTML> <html lang="zh"> <head> <meta charset="gb2312"> <title>圓角兼容性測試</title> <style type="text/css"> .body-radius-class1, .body-radius-class2{ margin: 10px 20px; padding: 10px 20px; } /* 圓角,沒解決兼容性 */ .body-radius-class1{ background: #FF0000; -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; } /* 圓角,解決兼容性 */ .body-radius-class2{ background: #8B4C39; -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; /* 使用PIE解決兼容性 */ behavior: url("PIE/PIE.htc"); } .body-gradient-class1, .body-gradient-class2{ margin: 10px 20px; padding: 10px 20px; } /* 漸變,沒解決兼容性 */ .body-gradient-class1{ background: #8EE5EE; background: -moz-linear-gradient(to bottom, #8EE5EE, #fff); background: -webkit-linear-gradient(to bottom, #8EE5EE, #fff); background: linear-gradient(to bottom, #8EE5EE, #fff); } /* 漸變,解決兼容性 */ .body-gradient-class2{ background: #8E8E8E; background: -moz-linear-gradient(to bottom, #8E8E8E, #fff); background: -webkit-linear-gradient(to bottom, #8E8E8E, #fff); background: linear-gradient(to bottom, #8E8E8E, #fff); /* 使用PIE解決兼容性 */ -pie-background: linear-gradient(#8E8E8E, #fff); behavior: url("PIE/PIE.htc"); } </style> <script type="text/javascript" src="jquery-2.2.3.min.js"></script> </head> <body> <h1>border-radius</h1> <div class="body-radius-class1"> <span>沒解決兼容性</span> </div> <div class="body-radius-class2"> <span>解決兼容性</span> </div> <h1>gradient</h1> <div class="body-gradient-class1"> <span>沒解決兼容性</span> </div> <div class="body-gradient-class2"> <span>解決兼容性</span> </div> </body> </html>
如果本篇內容對大家有幫助,請點擊頁面右下角的關注。如果覺得不好,也歡迎拍磚。你們的評價就是博主的動力!下篇內容,敬請期待!