大家可能會遇到子頁面內容較多但iframe高度不夠的情況。給iframe設置scrolling="no"的話子頁面內容顯示不全,不設置又會出現滾動條從而影響美觀。當我們點擊不同的菜單讓iframe加載不同的html文件時,iframe的高度就需要做相應的調整。
主體思路:子頁面加載完成后根據具體body的高度給iframe設置一個適合的高度
情況1:各個子頁面內容與高度比較固定
<script> $(function(){ $("#Frame_Content").load(function(){ var frame_content = $(this); //獲取子頁面body的高度 並適量增加 var mainheight = frame_content.contents().find("body").height()+30; //給iframe設置高度(不低於350) frame_content.height(Math.max(mainheight,350)); }); }); </script> <iframe class="main" onload="this.height=350" frameborder="0" scrolling="no" id="Frame_Content" name="content"></iframe>
這樣每次加載完子頁面,就可以根據實際的內容來給iframe設置相應的高度。
情況2:子頁面本身內容在不斷變化
有的情況下,子頁面發起Ajax請求,從后台拿到新的數據后增加或減少子頁面的內容,如果只是在iframe load完成后設定好高度依然沒辦法真正地動態調整。到這里,我想大家肯定第一反應是使用監聽事件。如果可以對子頁面body的高度進行監聽,那么每當內容有所調整的時候觸發事件並對iframe的高度進行設置。不過這里有個問題,window對象有onresize事件,但普通的dom元素是沒有的,也就是說想監聽也沒辦法監聽。想要解決這個問題,兩條路擺在我們面前:
1.寫一個輪詢來不停的獲取子頁面body的高度,如果發生了變化就給iframe高度賦新的值;
2.利用jquery的事件機制來模擬一個普通元素上的resize事件。(當然只要能實現resize事件,怎么弄都可以,這里只說jquery)
仔細來看,其實1和2的原理是一樣的,事件監聽本質上其實應該也是輪詢。但完整的事件機制肯定是比我們自己去寫一個輪詢要成熟的,而且適用所有的dom元素,如果自己寫個輪詢肯定能做的事就很單一,只有針對性的功能。因為我這里的頁面只有一個主iframe,外面就一個框架,橫向的主菜單和左側縱向的二級、三級菜單,頁面的主要內容都在iframe根據不同菜單加載的子頁面里,等於說只要把唯一的iframe高度調整ok就行。這里我們不妨先簡單搞一個輪詢:
<script> $(function(){ var timer; $("#Frame_Content").load(function(){ if (timer){ clearInterval(timer); } //pre_height用於記錄上次檢查時body的高度 //mainheight用於獲取本次檢查時body的高度,並賦予iframe的高度 var mainheight,pre_height; var frame_content = $(this); timer = setInterval(function(){ mainheight = frame_content.contents().find("body").height() + 30; if (mainheight != pre_height){ pre_height = mainheight; frame_content.height(Math.max(mainheight,350)); } },500);//每0.5秒檢查一次 }); }); </script>
如果場景比較簡單,一個輪詢就可以搞定iframe動態調整高度的問題,比起自己寫一個jquery的插件來實現resize事件肯定要輕量很多。但這樣的做法畢竟是不夠完善的,如果有的頁面需要對多個dom元素(不局限於iframe)的高度進行監聽,搞很多輪詢看起來可能很不規范,同時也不便於管理。
https://segmentfault.com/a/1190000000406026這篇文章里提到一個插件已經實現了上述的功能,並貼出了核心代碼。代碼長度很短也很簡單,感興趣的話可以研究一下,實際上插件也是使用輪詢來不斷地檢查所需要監聽的dom元素的高度和寬度,我也在下面貼出來:

(function($, window, undefined) { var elems = $([]), jq_resize = $.resize = $.extend($.resize, {}), timeout_id, str_setTimeout = 'setTimeout', str_resize = 'resize', str_data = str_resize + '-special-event', str_delay = 'delay', str_throttle = 'throttleWindow'; jq_resize[str_delay] = 250; jq_resize[str_throttle] = true; $.event.special[str_resize] = { setup: function() { if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; } var elem = $(this); elems = elems.add(elem); $.data(this, str_data, { w: elem.width(), h: elem.height() }); if (elems.length === 1) { loopy(); } }, teardown: function() { if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; } var elem = $(this); elems = elems.not(elem); elem.removeData(str_data); if (!elems.length) { clearTimeout(timeout_id); } }, add: function(handleObj) { if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; } var old_handler; function new_handler(e, w, h) { var elem = $(this), data = $.data(this, str_data); data.w = w !== undefined ? w : elem.width(); data.h = h !== undefined ? h : elem.height(); old_handler.apply(this, arguments); } if ($.isFunction(handleObj)) { old_handler = handleObj; return new_handler; } else { old_handler = handleObj.handler; handleObj.handler = new_handler; } } }; function loopy() { timeout_id = window[str_setTimeout](function() { elems.each(function() { var elem = $(this), width = elem.width(), height = elem.height(), data = $.data(this, str_data); if (width !== data.w || height !== data.h) { elem.trigger(str_resize, [data.w = width, data.h = height]); } }); loopy(); }, jq_resize[str_delay]); } })(jQuery, this);
jquery的事件機制和插件的寫法這里就不具體解析了,網上搜一下都可以找到。這方面我也沒有深入的學習,不過只要了解一下要點就能看明白上面的插件代碼了。
不想了解,也可以直接拿來用:
//bind resize事件 $("body").bind('resize',function(){ //callback //... //... });
ps:
正常情況下對頁面中的dom元素高寬進行監聽使用上面的插件應該是沒什么問題的,但如果有和我一樣是對iframe子頁面的body進行監聽可能會遇到報錯。
看過插件的源碼后,會發現當我們對n個dom元素bind resize事件時,實際上有一個隊列,輪詢中會對隊列的每一個元素都檢查高寬有沒有變化,如果高寬改變了就觸發resize事件,從而執行我們自己定義的回調函數。unbind后會把相應的元素從隊列中踢掉。我這里的場景是,每個菜單項的target都指向同一個iframe,點擊不同的菜單項主iframe加載不同的頁面。我在iframe完成load的時候給子頁面的body bind resize事件。當切換菜單時,新的子頁面加入隊列,但老的子頁面此時已經不存在了,遍歷隊列去取對應元素的高寬就會出錯。對插件的源碼進行修改可以解決這個問題,但我個人認為為了這個單一的目的去修改插件的源碼還不如最初的時候就用一個簡單的輪詢搞定。所以不改源碼的情況下,建議把bind放到子頁面中去做,回調中調用父頁面的接口來給iframe高度賦值。