顯示、可見度與透明度


網頁中經常做的特效,就是將元素隱藏或顯示,這在網頁上有幾種實作的方式,主要看需求而定。

可以透過設定 style 屬性的 display來顯示或隱藏元素,display 設定為 none 時,元素就會從畫面上消失,而且不列入排版考量,也就是畫面上看起來,元素原本所佔據的空間消失了,如果設定 displayblock 則會將元素以區塊方式顯示,像是段落與標題。設定 displayinline 則會將元素以行內方式顯示,像是 span 元素。

一個簡單的例子如下所示:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">

    <style type="text/css">
        #message {
            color: #ffffff;
            background-color: #ff0000;
            border-width: 10px;
            border-color: black;
            border-style: solid;
            width: 100px;
            height: 50px;
            padding: 50px;
        }
    </style>  
</head>
<body>

    <button id='toggle'>切換顯示狀態</button>
    <hr>
    這是一些文字!這是一些文字!這是一些文字!這是一些文字!這是一些文字!
    <div id="message">這是訊息一</div>
    這是其他文字!這是其他文字!這是其他文字!這是其他文字!這是其他文字!

<script type="text/javascript">

    function style(elem, prop) {
        return window.getComputedStyle(elem, null)[prop];
    }

    function show(elem) {
        elem.style.display = elem.previousDisplay || '';
        if(style(elem, 'display') === 'none') {
            // 在 DOM 樹上建立元素,取得 display 預設值後移除
            let node = document.createElement(elem.nodeName);
            document.body.appendChild(node);
            elem.style.display = style(node, 'display');
            document.body.removeChild(node);
        }
    }

    function hide(elem) {
        elem.previousDisplay = style(elem, 'display');
        elem.style.display = 'none';
    }

    document.getElementById('toggle').onclick = function() {
        let message = document.getElementById('message');
        if(style(message, 'display') === 'none') {
            show(message);
        }
        else {
            hide(message);
        }
    };
</script>  

</body>
</html>

按我觀看執行結果

在上例中,透過將 display 設定為none來隱藏元素,並記錄原有的 display 值,而顯示元素時,若有記錄原 display 值則恢復為原本的值,否則就使用元素預 設的值,例如 <div> 會設定為 block,而 <span> 會設定為 inline

設定 stylevisibilityvisiblehidden 亦可顯示或隱藏元素,元素的 visibility 被設為 hidden 時,雖然畫面上看不見,但排版仍會考慮它,也就是元素在畫面上仍會佔有一塊空間。

就結論而言,display 其實是指排版上的設定,設定為 none 時,表示排版上不考慮,既然不考慮排版,所以也就看不見,visibility 只是單純設定視覺效果。所以 display 設定為 none,而 visibility 設定為 visible 時,也是看不見元素的,因為排版上不考慮,而 display 不是 none,而 visibilityhidden 時,排版上會考慮,所以空間會佔據,但看不到元素。

另一個亦可影響元素視覺效果的是元素的不透明度,元素的不透明度為 0 時,元素當然就完全看不見了,不過,通常不會用不透明度來顯示或隱藏元素,而會用來作半透明、淡出、淡入的效果。

要設定元素的不透明度,標準上是指定 0 到 1 間的數值給 styleopacity,1 表示完全不透明,0 就是透明了。

function opacity(elem, value) {
    elem.style.opacity = value;
}

元素的不透明度為 0 時,雖然看不到元素,但排版上仍會考慮它的存在,所以元素仍然佔據空間。下面這個例子,實作了簡單的淡出、淡入,如果元素本身有設定不透明度,淡入時會回復至原有的不透明度:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <button id='fadeOut'>淡出</button>
    <button id='fadeIn'>淡入</button><br>
    <img id="image" src="https://openhome.cc/Gossip/images/caterpillar_small.jpg">  

<script type="text/javascript">
    function style(elem, prop) {
        return window.getComputedStyle(elem, null)[prop];
    }

    // value 未指定時,用來取得不透明度
    function opacity(elem, value) {
        if(value === undefined) { 
            let opt = style(elem, 'opacity');
            return opt === '' ? 1 : parseFloat(opt);
        } else {
            elem.style.opacity = value;
        }
    }

    // speed 是淡入總時間,step 是動畫數
    function fadeIn(elem, speed = 5000, steps = 10) {
        let targetValue = elem.previousOpacity || 1;
        delete elem.previousOpacity;

        let timeInterval = speed / steps;
        let valueStep = targetValue / steps;

        let opt = 0;
        setTimeout(function next() {
            opt += valueStep;
            if(opt < targetValue) {
                opacity(elem, opt);
                setTimeout(next, timeInterval);
            }
            else {
                opacity(elem, targetValue);
            }
        }, timeInterval);
    }     

    function fadeOut(elem, speed = 5000, steps = 10) {
        elem.previousOpacity = opacity(elem);

        let timeInterval = speed / steps;
        let valueStep = elem.previousOpacity / steps;

        let opt = elem.previousOpacity;
        setTimeout(function next() {
            opt -= valueStep;
            if(opt > 0) {
                opacity(elem, opt);
                setTimeout(next, timeInterval);
            }
            else {
                opacity(elem, 0);
            }
        }, timeInterval);
    }  

    let image = document.getElementById('image');

    document.getElementById('fadeIn').onclick = function() {
        fadeIn(image, 2000);
    };

    document.getElementById('fadeOut').onclick = function() {
        fadeOut(image, 2000);
    };  
</script>

  </body>
</html>

按我觀看執行結果