MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript处理文档视口大小变化

2023-03-252.7k 阅读

一、JavaScript 与文档视口

在现代网页开发中,适配不同设备的屏幕尺寸是至关重要的。文档视口(viewport)代表了浏览器窗口中可用于显示网页内容的区域,随着设备的多样化,处理文档视口大小的变化成为了 JavaScript 开发者必须掌握的技能。

JavaScript 提供了多种方式来检测和响应文档视口大小的变化,这对于创建响应式网页设计起着关键作用。响应式设计旨在使网页在不同的设备(如桌面电脑、平板电脑和手机)上都能提供良好的用户体验。

二、检测视口大小变化的方法

(一)window.onresize 事件

  1. 基本原理window.onresize 事件会在窗口大小发生改变时触发。每当用户调整浏览器窗口的宽度或高度时,绑定到该事件的函数就会被执行。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Window Resize Example</title>
</head>

<body>
    <p id="message">当前窗口大小:</p>
    <script>
        window.onresize = function () {
            const width = window.innerWidth;
            const height = window.innerHeight;
            document.getElementById('message').innerHTML = `当前窗口大小:宽 ${width}px,高 ${height}px`;
        };
    </script>
</body>

</html>

在上述代码中,当窗口大小改变时,window.onresize 事件的回调函数会获取当前窗口的内部宽度(window.innerWidth)和内部高度(window.innerHeight),并更新页面上的文本,显示当前窗口的尺寸。

(二)addEventListener('resize', callback)

  1. 基本原理addEventListener 方法是一种更灵活的事件绑定方式。它允许为同一个事件绑定多个回调函数,相比 window.onresize 只能绑定一个函数,这种方式在大型项目中更为实用。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Add Event Listener Resize Example</title>
</head>

<body>
    <p id="sizeInfo">窗口尺寸:</p>
    <script>
        function updateSizeInfo() {
            const width = window.innerWidth;
            const height = window.innerHeight;
            document.getElementById('sizeInfo').innerHTML = `窗口尺寸:宽 ${width}px,高 ${height}px`;
        }

        window.addEventListener('resize', updateSizeInfo);
    </script>
</body>

</html>

在这段代码中,我们定义了一个 updateSizeInfo 函数,然后使用 addEventListener 将该函数绑定到 resize 事件上。这样,每当窗口大小改变时,updateSizeInfo 函数就会被调用,更新页面上显示的窗口尺寸信息。

三、根据视口大小进行布局调整

(一)简单的布局切换

  1. 原理:通过检测视口大小,我们可以决定显示或隐藏某些元素,或者改变元素的样式,以适应不同的屏幕尺寸。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        .wide - only {
            display: block;
        }

        .narrow - only {
            display: none;
        }
    </style>
    <title>Layout Adjustment Example</title>
</head>

<body>
    <div class="wide - only" id="wideContent">这是宽屏显示的内容</div>
    <div class="narrow - only" id="narrowContent">这是窄屏显示的内容</div>
    <script>
        function adjustLayout() {
            const width = window.innerWidth;
            const wideContent = document.getElementById('wideContent');
            const narrowContent = document.getElementById('narrowContent');
            if (width >= 768) {
                wideContent.style.display = 'block';
                narrowContent.style.display = 'none';
            } else {
                wideContent.style.display = 'none';
                narrowContent.style.display = 'block';
            }
        }

        window.addEventListener('resize', adjustLayout);
        adjustLayout();// 初始化布局
    </script>
</body>

</html>

在上述代码中,我们定义了两个 CSS 类 .wide - only.narrow - only,分别用于宽屏和窄屏显示。通过 adjustLayout 函数,根据窗口宽度判断是宽屏还是窄屏,然后相应地显示或隐藏对应的内容块。页面加载时会调用一次 adjustLayout 函数进行初始化布局,之后窗口大小改变时也会调用该函数调整布局。

(二)使用 CSS 媒体查询结合 JavaScript

  1. 原理:CSS 媒体查询是一种强大的响应式设计工具,它可以根据不同的设备特性(如屏幕宽度、高度、分辨率等)应用不同的样式。JavaScript 可以与媒体查询结合,进一步增强布局的灵活性。例如,我们可以通过 JavaScript 检测当前是否匹配某个媒体查询条件,然后执行相应的操作。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        @media (max - width: 768px) {
            body {
                background - color: lightblue;
            }
        }

        @media (min - width: 769px) {
            body {
                background - color: lightgreen;
            }
        }
    </style>
    <title>Media Query with JavaScript</title>
</head>

<body>
    <p id="mediaQueryInfo">当前媒体查询状态:</p>
    <script>
        const mediaQuerySmall = window.matchMedia('(max - width: 768px)');
        const mediaQueryLarge = window.matchMedia('(min - width: 769px)');

        function updateMediaQueryInfo() {
            let message = '';
            if (mediaQuerySmall.matches) {
                message = '当前屏幕宽度小于等于 768px';
            } else if (mediaQueryLarge.matches) {
                message = '当前屏幕宽度大于 768px';
            }
            document.getElementById('mediaQueryInfo').innerHTML = message;
        }

        mediaQuerySmall.addEventListener('change', updateMediaQueryInfo);
        mediaQueryLarge.addEventListener('change', updateMediaQueryInfo);
        updateMediaQueryInfo();// 初始化信息
    </script>
</body>

</html>

在这段代码中,我们首先定义了两个 CSS 媒体查询,分别针对宽度小于等于 768px 和大于 768px 的屏幕设置不同的背景颜色。然后使用 window.matchMedia 方法获取这两个媒体查询的状态,并为它们的 change 事件绑定 updateMediaQueryInfo 函数。该函数会根据当前匹配的媒体查询条件更新页面上显示的信息。页面加载时会调用一次 updateMediaQueryInfo 函数进行初始化。

四、视口大小变化与性能优化

(一)防抖(Debounce)

  1. 原理:当窗口大小改变时,resize 事件会频繁触发。如果在每次触发事件时都执行复杂的操作,可能会导致性能问题,特别是在移动设备上。防抖技术可以解决这个问题,它的原理是设置一个延迟时间,在延迟时间内如果事件再次触发,则重新计时,只有在延迟时间结束后事件没有再次触发,才执行实际的操作。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Debounce Example</title>
</head>

<body>
    <p id="debounceMessage">窗口大小变化信息:</p>
    <script>
        function debounce(func, delay) {
            let timer;
            return function () {
                const context = this;
                const args = arguments;
                clearTimeout(timer);
                timer = setTimeout(() => {
                    func.apply(context, args);
                }, delay);
            };
        }

        function updateDebounceMessage() {
            const width = window.innerWidth;
            const height = window.innerHeight;
            document.getElementById('debounceMessage').innerHTML = `窗口大小:宽 ${width}px,高 ${height}px`;
        }

        const debouncedUpdate = debounce(updateDebounceMessage, 300);
        window.addEventListener('resize', debouncedUpdate);
    </script>
</body>

</html>

在上述代码中,我们定义了一个 debounce 函数,它接受一个要执行的函数 func 和延迟时间 delaydebounce 函数返回一个新的函数,在这个新函数中,每次触发事件时都会清除之前设置的定时器,并重新设置一个新的定时器。只有在延迟时间(这里是 300 毫秒)内没有再次触发事件,才会执行实际的 updateDebounceMessage 函数,从而减少了频繁触发 resize 事件导致的性能开销。

(二)节流(Throttle)

  1. 原理:节流与防抖类似,也是为了控制事件触发的频率。节流的原理是设置一个时间间隔,在这个时间间隔内,无论事件触发多少次,都只执行一次实际操作。这可以确保在窗口大小持续改变时,操作不会过于频繁地执行。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Throttle Example</title>
</head>

<body>
    <p id="throttleMessage">窗口大小变化信息:</p>
    <script>
        function throttle(func, interval) {
            let lastTime = 0;
            return function () {
                const context = this;
                const args = arguments;
                const now = new Date().getTime();
                if (now - lastTime >= interval) {
                    func.apply(context, args);
                    lastTime = now;
                }
            };
        }

        function updateThrottleMessage() {
            const width = window.innerWidth;
            const height = window.innerHeight;
            document.getElementById('throttleMessage').innerHTML = `窗口大小:宽 ${width}px,高 ${height}px`;
        }

        const throttledUpdate = throttle(updateThrottleMessage, 500);
        window.addEventListener('resize', throttledUpdate);
    </script>
</body>

</html>

在这段代码中,我们定义了一个 throttle 函数,它接受一个要执行的函数 func 和时间间隔 intervalthrottle 函数返回一个新的函数,在这个新函数中,通过记录上一次执行的时间 lastTime,每次触发事件时检查当前时间与上一次执行时间的间隔是否大于设定的时间间隔(这里是 500 毫秒)。如果大于,则执行实际的 updateThrottleMessage 函数,并更新 lastTime,从而实现了在一定时间间隔内只执行一次操作,避免了因频繁触发 resize 事件而导致的性能问题。

五、跨浏览器兼容性

不同的浏览器在处理视口大小变化时可能存在一些差异。例如,某些较旧的浏览器可能对 addEventListener 方法的支持不完全,或者在获取窗口尺寸的属性上有不同的行为。

为了确保代码在各种浏览器上都能正常工作,我们可以使用一些兼容性库,如 jQuery。jQuery 提供了统一的事件绑定和尺寸获取方法,大大简化了跨浏览器开发。

  1. 使用 jQuery 检测视口大小变化
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://code.jquery.com/jquery - 3.6.0.min.js"></script>
    <title>jQuery Resize Example</title>
</head>

<body>
    <p id="jqueryMessage">当前窗口大小:</p>
    <script>
        $(window).resize(function () {
            const width = $(window).width();
            const height = $(window).height();
            $('#jqueryMessage').html(`当前窗口大小:宽 ${width}px,高 ${height}px`);
        });
    </script>
</body>

</html>

在上述代码中,我们引入了 jQuery 库,然后使用 $(window).resize 方法绑定了一个处理函数。当窗口大小改变时,该函数会通过 $(window).width()$(window).height() 获取当前窗口的宽度和高度,并更新页面上的信息。这种方式在不同浏览器上具有更好的兼容性。

此外,在处理跨浏览器兼容性时,还需要注意以下几点:

  • IE 浏览器:IE 8 及以下版本不支持 addEventListener 方法,需要使用 attachEvent 方法替代。
  • 移动浏览器:不同的移动浏览器在处理视口大小变化时可能有不同的性能表现和事件触发机制。例如,某些移动浏览器在用户缩放页面时可能不会触发 resize 事件,需要使用其他方式来检测缩放变化。

六、实际应用场景

(一)响应式导航栏

  1. 原理:在移动设备上,由于屏幕空间有限,通常需要将导航栏切换为折叠式或汉堡菜单的形式。通过检测视口大小,当屏幕宽度小于某个阈值时,将导航栏隐藏并显示汉堡菜单按钮,用户点击按钮后展开导航栏。当屏幕宽度大于阈值时,恢复正常的水平导航栏布局。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        nav {
            background - color: #333;
            color: white;
            padding: 10px;
        }

        nav ul {
            list - style - type: none;
            margin: 0;
            padding: 0;
            display: flex;
        }

        nav ul li {
            margin - right: 20px;
        }

        .hamburger {
            display: none;
            cursor: pointer;
        }

        .hamburger .bar {
            width: 25px;
            height: 3px;
            background - color: white;
            margin: 6px 0;
        }

        @media (max - width: 768px) {
            nav ul {
                display: none;
            }

            .hamburger {
                display: block;
            }
        }
    </style>
    <title>Responsive Navbar Example</title>
</head>

<body>
    <nav>
        <div class="hamburger" id="hamburger">
            <div class="bar"></div>
            <div class="bar"></div>
            <div class="bar"></div>
        </div>
        <ul id="navList">
            <li><a href="#">首页</a></li>
            <li><a href="#">关于</a></li>
            <li><a href="#">服务</a></li>
            <li><a href="#">联系我们</a></li>
        </ul>
    </nav>
    <script>
        const hamburger = document.getElementById('hamburger');
        const navList = document.getElementById('navList');

        function toggleNav() {
            if (navList.style.display === 'none' || navList.style.display === '') {
                navList.style.display = 'flex';
            } else {
                navList.style.display = 'none';
            }
        }

        hamburger.addEventListener('click', toggleNav);

        function adjustNavbar() {
            const width = window.innerWidth;
            if (width <= 768) {
                navList.style.display = 'none';
            } else {
                navList.style.display = 'flex';
            }
        }

        window.addEventListener('resize', adjustNavbar);
        adjustNavbar();// 初始化布局
    </script>
</body>

</html>

在上述代码中,我们首先通过 CSS 媒体查询设置了在宽度小于等于 768px 时隐藏导航栏列表并显示汉堡菜单按钮。然后通过 JavaScript 为汉堡菜单按钮添加点击事件,点击时切换导航栏列表的显示状态。同时,通过 window.addEventListener('resize', adjustNavbar) 检测窗口大小变化,当窗口宽度小于等于 768px 时隐藏导航栏列表,大于 768px 时显示导航栏列表,从而实现了响应式导航栏的功能。

(二)图片自适应

  1. 原理:不同尺寸的设备对图片的需求不同。在大屏幕设备上可以显示高分辨率的图片以提供更好的视觉效果,而在小屏幕设备上显示低分辨率的图片可以减少加载时间和流量消耗。通过检测视口大小,我们可以根据不同的屏幕尺寸加载不同分辨率的图片。
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Images Example</title>
</head>

<body>
    <img id="responsiveImage" src="small.jpg" alt="Responsive Image">
    <script>
        function updateImage() {
            const width = window.innerWidth;
            const image = document.getElementById('responsiveImage');
            if (width >= 768) {
                image.src = 'large.jpg';
            } else {
                image.src ='small.jpg';
            }
        }

        window.addEventListener('resize', updateImage);
        updateImage();// 初始化图片
    </script>
</body>

</html>

在这段代码中,我们首先在页面上放置了一个图片元素,并初始设置其 src 属性为 small.jpg。然后通过 updateImage 函数,根据窗口宽度判断是否大于等于 768px,如果是,则将图片的 src 属性更新为 large.jpg,否则保持为 small.jpg。通过 window.addEventListener('resize', updateImage) 监听窗口大小变化,实现了图片根据视口大小自适应加载不同分辨率图片的功能。

通过以上多种方法和示例,我们可以全面地掌握 JavaScript 处理文档视口大小变化的技术,从而创建出更加灵活、高效且用户体验良好的网页应用程序。无论是简单的布局调整还是复杂的交互效果,视口大小变化的处理都是现代网页开发中不可或缺的一部分。在实际项目中,我们需要根据具体需求选择合适的方法,并注意性能优化和跨浏览器兼容性,以确保网页在各种设备上都能正常运行。