於本篇中,將介紹操控視窗與捲軸的一些方法。
透過 window 物件的 open 方法,可以幫你開啟一個新的視窗,並且設定該視窗的相關樣貌,例如:
- toolbar: 工具列
- location: 網址列
- status: 網址列
- menubar: 功能表列
- scrollbars: 捲軸
- resizable: 放大縮小
- width: 寬度
- height: 高度
以上的參數設定方式,除了 width 和 height 是以數字指定之外,其餘都是用 yes/no 或者 1/0 來指定是否呈現。另外,這些顯示效果根據瀏覽器的不同,可能也會有所不同,例如 Chorme 一定會顯示網址列和捲軸。以下範例會開啟一個寬度和高度各是螢幕一半的視窗:
<html> <head></head> <body bgcolor="#ccccff"> <script> function myOpen(){ window.open( "../index.htm", "test", "width=" + screen.width / 2 + ", height=" + screen.height / 2 ); } </script> <input type="button" onClick="myOpen();" value="按我開新視窗"> </body></html>
利用「close」方法,可以關閉視窗,範例如下:
<html> <head></head> <body bgcolor="#ccccff"> <input type="button" onClick="window.close();" value="按我關閉視窗"> </body></html>
在上述範例中,如果你先造訪了其他網頁再來到範例頁面,則會發現視窗無法關閉,這是基於安全性的考量,如果該視窗有瀏覽其他網頁的記錄(也就是你能按上一頁/下一頁的時候),則可能會無法關閉視窗。
透過 opener,可以操作原來的視窗。我們先使用一個網頁做為母視窗,用來開啟子視窗:
<html> <head></head> <body bgcolor="#ccccff"> <script> function myOpen(){ window.open("js_window_opener_menu.htm", "test"); } </script> <input type="button" onClick="myOpen();" value="按我開新視窗"> </body></html>
子視窗的內容如下,此例以 opener.location.href 控制母視窗所開啟的網址:
<html> <head></head> <body bgcolor="#ccccff"> <script> function mainCtrl(theUrl){ if(opener == null){ alert("母視窗已關閉或者不存在"); } else { opener.location.href = theUrl; } } </script> <a href="javascript:mainCtrl('http://www.nthu.edu.tw')">清大首頁</a><br/> <a href="javascript:mainCtrl('http://www.nctu.edu.tw')">交大首頁</a> </body></html>
上述範例中,也可以改為在偵測到母視窗關閉後,重新開啟一個視窗,並改為對新的視窗操作。另外,各位也可以試試看直接開啟子視窗的效果。
如果希望呈現的效果不是子母視窗的互相操控,而是把一個網頁畫面進行切割,以將其他網頁內嵌進來互相操控,那麼可以透過 iframe 標籤進行(古早時代是利用 frameset 和 frame 標籤,但 HTML5 已不支援這兩個標籤)。此時,如果你要從某個 iframe 操作其他的 iframe,則除了利用超連結的 target 屬性以外,還可以使用 JavaScript 來進行。下面這個範例,用兩個 iframe 各自嵌入不同的網頁,並且可利用嵌入在左邊的網頁,操控嵌入在右邊的網頁,如下:
<html> <head></head> <body bgcolor="#ccccff"> <table width="100%" height="100%" border="1"> <tr> <td width="20%"> <iframe name="menu" src="js_window_frame_menu.htm" width="100%" height="100%"></iframe> </td> <td width="80%"> <iframe name="right" src="js_window_frame_right.htm" width="100%" height="100%"></iframe> </td> </tr> </table> </body></html>
其中,被嵌入為左邊的網頁如下:
<html> <head></head> <body bgcolor="#ccccff"> <a href="http://www.nthu.edu.tw" target="right">清大首頁</a><br> <a href="http://www.nctu.edu.tw" target="right">交大首頁</a><br> <input type="button" value="原來那頁" onclick="parent.right.location.href='js_window_frame_right.htm';"> </body></html>
被嵌入為右邊的網頁則如下:
<html> <head></head> <body bgcolor="#ccccff"> <script> document.write(Math.random()); </script> </body></html>
操作瀏覽紀錄也是在 windows 物件的業務範圍之內。你除了可透過 window.history.go 來前往上下幾頁以外,在 HTML5 中還加入了 pushState 等方法,可以用來加入和修改歷史記錄。如果搭配上物件樣式等方面的改變,則可以達成類似臉書點擊照片或者低卡點擊文章時的效果:
<html> <head> <style> #slideShow { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: none; z-index: 1; } #slideShowImg { position: absolute; top: 100px; left: 350px; z-index: 2; display: none; } </style> </head> <body bgcolor="#ccccff"> <script> function showSlide(path){ document.getElementById("slideShowImg").src = path; document.getElementById("slideShow").style.display = "block"; document.getElementById("slideShowImg").style.display = "block"; window.history.pushState( { "what_is_this": "隨便放一些你自己認得出 state 的東西" }, "這裡是標題,但是多數瀏覽器仍未支援", path ); } function hideSlide(){ document.getElementById("slideShow").style.display = "none"; document.getElementById("slideShowImg").style.display = "none"; window.history.back(); } window.onpopstate = function(){hideSlide();} </script> <img src="../pics/bg01.jpg" width="300" onClick="showSlide('../pics/bg01.jpg')"> <div id="slideShow" onClick="hideSlide()"></div> <img id="slideShowImg" src=""> </body></html>
若要改變已開啟視窗的位置或大小,可以使用 moveTo, moveBy, resizeTo, 以及 resizeBy。但請留意,除了 IE 之外的瀏覽器,這些函式只能對 window.open 開啟的視窗操作,若有興趣可自行測試:
<html> <head></head> <body bgcolor="#ccccff"> <script> function winCtrl(funcName){ x = document.getElementById("X").value; y = document.getElementById("Y").value; eval("newWindow." + funcName + "(" + x + ", " + y + ");"); } </script> <form> <input type="button" value="先按我開新視窗" onclick="newWindow = window.open('../index.htm','OAO','width=300,height=300');"> X: <input id="X" type="number" min="50" max="1000" value="50"><br/> Y: <input id="Y" type="number" min="50" max="1000" value="50"><br/> <input type="button" value="moveTo" onclick="winCtrl(this.value)"> <input type="button" value="moveBy" onclick="winCtrl(this.value)"> <input type="button" value="resizeTo" onclick="winCtrl(this.value)"> <input type="button" value="resizeBy" onclick="winCtrl(this.value)"> </form> </body></html>
若要控制視窗的捲動,可以使用 scroll, scrollTo, 以及 scrollBy:
<html> <head></head> <body bgcolor="#ccccff"> <script> function winCtrl(funcName){ x = document.getElementById("X").value; y = document.getElementById("Y").value; eval("window." + funcName + "(" + x + ", " + y + ");"); } </script> <form style="position:fixed;top:10px;left:10px;background:#cfc;padding:10px;"> X: <input id="X" type="number" min="0" max="1000" value="50"><br/> Y: <input id="Y" type="number" min="0" max="1000" value="50"><br/> <input type="button" value="scroll" onclick="winCtrl(this.value)"> <input type="button" value="scrollTo" onclick="winCtrl(this.value)"> <input type="button" value="scrollBy" onclick="winCtrl(this.value)"> </form> <div style="position:absolute;top:1500px;left:1500px;"> 這是純粹把文件範圍撐大的 div </div> </body></html>
監聽 scroll 事件可以在捲軸捲動時做出你想要的處理,搭配上取得捲軸位置與物件高度的判斷,就可以做出無限捲動的效果:
<html> <head> <style> table { width: 90%; border-collapse: collapse; } td { border: 1px solid; width: 15%; padding-top: 100px; padding-bottom: 100px; text-align: center; } </style> </head> <body bgcolor="#ccccff"> <center><table id="numTable"></table></center> <script> var begin = 1; function getTr(){ cnt = '<tr>'; for(count=0; count<5; count++, begin++){ cnt += '<td>' + begin + '</td>'; } cnt += '</tr>'; return cnt; } function makeNewRows(){ document.getElementById('numTable').innerHTML += getTr(); document.getElementById('numTable').innerHTML += getTr(); document.getElementById('numTable').innerHTML += getTr(); document.getElementById('numTable').innerHTML += getTr(); document.getElementById('numTable').innerHTML += getTr(); } window.onload = function(){ document.getElementById('numTable').innerHTML = ''; makeNewRows(); } window.addEventListener('scroll', () => { if(window.innerHeight + window.pageYOffset > document.body.offsetHeight-5){ makeNewRows(); } }); </script> </body></html>
圖片延遲載入(lazy loading)的概念也非常類似,是等到圖片進入視窗範圍後再將其載入,以避免網頁在一開始就花時間在載入要捲很久才能看見的資源。Lazy loading 除了可以用已經在 Chrome 等部分瀏覽器支援的原生功能,或者第三方套件實作出來以外,也可以用比較基本的監聽事件來完成。在這個應用中,除了像無限捲動一樣,每當滑到底時再依次新增內容以外,也可以在每次捲動時,用 getBoundingClientRect 判斷圖片是否已經允許載入(為求範例簡潔故只針對高度做判斷,若需判斷寬度請依此類推):
<html> <head></head> <body bgcolor="#ccccff"> <center> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1605_01_魁力屋.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1605_02_橫綱.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1605_03_極雞.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_01_五行.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_02_若王子.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_03_天天有.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_04_arajin.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_05_boogie.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_01_珍遊.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_02_和釀.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_03_あくた川.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_04_山元.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1608_01_roppongi.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1608_02_池袋二郎.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1609_01_麵鬥庵.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1610_01_豬一.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1610_02_元喜神.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1612_01_若狹家.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1703_01_夕日.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1704_01_大文字.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1712_01_麵屋緣.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1802_01_小川.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1802_02_雞二.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1803_01_貓村.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1807_01_車廠.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1812_01_一燈.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1902_01_一慶.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1904_01_千雲.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1905_01_麵屋輝.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2007_01_京都柚子.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2007_02_中山.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2010_01_山下.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2011_01_你回來了.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2101_01_市民.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2102_01_隱家.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2103_01_柑橘.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2104_01_壹之穴.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2104_02_蘭丸.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2105_01_裸湯.JPG"><br> </center> <script> function processLazyImgs(){ lazyImgs = document.querySelectorAll('img.lazy'); for(i=0; i<lazyImgs.length; i++){ var pos = lazyImgs[i].getBoundingClientRect(); if(pos.top >= 0 && pos.top <= window.innerHeight - 100){ lazyImgs[i].src = lazyImgs[i].getAttribute('data-src'); lazyImgs[i].classList.remove("lazy"); lazyImgs[i].style.height = '500px'; lazyImgs[i].style.margin = '5px'; } } } window.onload = processLazyImgs window.addEventListener('scroll', processLazyImgs); </script> </body></html>
我們也可以將上述範例,改成用 Intersection Observer API 來監視物件是否已經進入視窗範圍,這樣的好處是節省瀏覽器內部運算效能,你也不用自己撰寫相關的判斷
<html> <head></head> <body bgcolor="#ccccff"> <center> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1605_01_魁力屋.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1605_02_橫綱.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1605_03_極雞.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_01_五行.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_02_若王子.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_03_天天有.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_04_arajin.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1606_05_boogie.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_01_珍遊.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_02_和釀.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_03_あくた川.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1607_04_山元.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1608_01_roppongi.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1608_02_池袋二郎.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1609_01_麵鬥庵.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1610_01_豬一.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1610_02_元喜神.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1612_01_若狹家.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1703_01_夕日.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1704_01_大文字.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1712_01_麵屋緣.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1802_01_小川.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1802_02_雞二.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1803_01_貓村.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1807_01_車廠.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1812_01_一燈.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1902_01_一慶.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1904_01_千雲.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/1905_01_麵屋輝.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2007_01_京都柚子.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2007_02_中山.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2010_01_山下.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2011_01_你回來了.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2101_01_市民.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2102_01_隱家.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2103_01_柑橘.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2104_01_壹之穴.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2104_02_蘭丸.JPG"><br> <img class="lazy" src="../pics/gray.png" data-src="../pics/ramen_and_cats/2105_01_裸湯.JPG"><br> </center> <script> watcher = new IntersectionObserver((objs, observer) => { objs.forEach(obj => { if(obj.isIntersecting){ img = obj.target; img.src = img.getAttribute('data-src'); img.classList.remove("lazy"); img.style.height = '500px'; img.style.margin = '5px'; observer.unobserve(img); } }) }); document.querySelectorAll('img.lazy').forEach(img => { watcher.observe(img); }); </script> </body></html>