etc./StackOverFlow

스크롤 후 요소가 보이는지 확인하는 방법은 무엇입니까?

청렴결백한 만능 재주꾼 2023. 4. 25. 10:55
반응형

질문자 :yoavf


AJAX를 통해 요소를 로드하고 있습니다. 일부는 페이지를 아래로 스크롤해야 볼 수 있습니다. 요소가 이제 페이지의 보이는 부분에 있는지 알 수 있는 방법이 있습니까?



이것은 트릭을 수행해야합니다.

 function isScrolledIntoView(elem) { var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elemTop = $(elem).offset().top; var elemBottom = elemTop + $(elem).height(); return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); }

간단한 유틸리티 기능 이 기능을 사용하면 찾고 있는 요소를 허용하는 유틸리티 기능을 호출할 수 있으며 요소가 완전히 또는 부분적으로 표시되도록 할 수 있습니다.

 function Utils() { } Utils.prototype = { constructor: Utils, isElementInView: function (element, fullyInView) { var pageTop = $(window).scrollTop(); var pageBottom = pageTop + $(window).height(); var elementTop = $(element).offset().top; var elementBottom = elementTop + $(element).height(); if (fullyInView === true) { return ((pageTop < elementTop) && (pageBottom > elementBottom)); } else { return ((elementTop <= pageBottom) && (elementBottom >= pageTop)); } } }; var Utils = new Utils();

용법

 var isElementInView = Utils.isElementInView($('#flyout-left-container'), false); if (isElementInView) { console.log('in view'); } else { console.log('out of view'); }

Scott Dowding

바닐라의 이 답변:

 function isScrolledIntoView(el) { var rect = el.getBoundingClientRect(); var elemTop = rect.top; var elemBottom = rect.bottom; // Only completely visible elements return true: var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight); // Partially visible elements return true: //isVisible = elemTop < window.innerHeight && elemBottom >= 0; return isVisible; }

bravedick

업데이트: IntersectionObserver 사용


내가 지금까지 찾은 최고의 방법은 jQuery 출현 플러그인 입니다. 매력처럼 작동합니다.

요소가 보기로 스크롤되거나 사용자에게 표시될 때 발생하는 사용자 지정 "표시" 이벤트를 모방합니다.

 $('#foo').appear(function() { $(this).text('Hello world'); });

이 플러그인은 숨겨져 있거나 볼 수 있는 영역 밖에 있는 콘텐츠에 대한 불필요한 요청을 방지하는 데 사용할 수 있습니다.


Joe Lencioni

스크롤 가능한 컨테이너 안에 숨겨진 경우에도 작동하는 순수 JavaScript 솔루션이 있습니다.

여기 데모 (창 크기 조정도 시도)

 var visibleY = function(el){ var rect = el.getBoundingClientRect(), top = rect.top, height = rect.height, el = el.parentNode // Check if bottom of the element is off the page if (rect.bottom < 0) return false // Check its within the document viewport if (top > document.documentElement.clientHeight) return false do { rect = el.getBoundingClientRect() if (top <= rect.bottom === false) return false // Check if the element is out of view due to a container scrolling if ((top + height) <= rect.top) return false el = el.parentNode } while (el != document.body) return true };

편집 2016-03-26: 요소를 지나서 스크롤하는 것을 설명하도록 솔루션을 업데이트하여 스크롤 가능한 컨테이너의 상단 위에 숨겨졌습니다. 편집 2018-10-08: 화면 위의 보기 밖으로 스크롤할 때 처리하도록 업데이트되었습니다.


Ally

IntersectionObserver API 사용

(최신 브라우저의 기본)


관찰자 를 사용하여 요소가 뷰포트 또는 스크롤 가능한 컨테이너에 표시되는지 확인하는 것은 쉽고 효율적입니다.

scroll 이벤트를 첨부하고 이벤트 콜백을 수동으로 확인할 필요가 없으므로 더 효율적입니다.

 // define an observer instance var observer = new IntersectionObserver(onIntersection, { root: null, // default is the viewport threshold: .5 // percentage of taregt's visible area. Triggers "onIntersection" }) // callback is called on intersection change function onIntersection(entries, opts){ entries.forEach(entry => entry.target.classList.toggle('visible', entry.isIntersecting) ) } // Use the bserver to observe an element observer.observe( document.querySelector('.box') ) // To stop observing: // observer.unobserve(entry.target)
 span{ position:fixed; top:0; left:0; } .box{ width:100px; height:100px; background:red; margin:1000px; transition:.75s; } .box.visible{ background:green; border-radius:50%; }
 <span>Scroll both Vertically &amp; Horizontally...</span> <div class='box'></div>


브라우저 지원 테이블 보기 (IE/Safari에서는 지원되지 않음)


vsync

jQuery Waypoints 플러그인은 여기에 아주 잘 어울립니다.

 $('.entry').waypoint(function() { alert('You have scrolled to an entry.'); });

플러그인 사이트에 몇 가지 예가 있습니다.


Fedir RYKHTIK

el )가 스크롤 가능한 div( holder )에 표시되는지 확인하기 위한 일반 바닐라

 function isElementVisible (el, holder) { holder = holder || document.body const { top, bottom, height } = el.getBoundingClientRect() const holderRect = holder.getBoundingClientRect() return top <= holderRect.top ? holderRect.top - top <= height : bottom - holderRect.bottom <= height }

jQuery와 함께 사용:

 var el = $('tr:last').get(0); var holder = $('table').get(0); var isVisible = isScrolledIntoView(el, holder);

Denis Matafonov

어때요

 function isInView(elem){ return $(elem).offset().top - $(window).scrollTop() < $(elem).height() ; }

그런 다음 요소가 다음과 같이 표시되면 원하는 대로 트리거할 수 있습니다.

 $(window).scroll(function(){ if (isInView($('.classOfDivToCheck'))) //fire whatever you what dothis(); })

그것은 나를 위해 잘 작동합니다.


webicy

내 요구 사항에 대한 Tweeked Scott Dowding의 멋진 기능 - 이것은 요소가 화면으로 스크롤되었는지 즉, 위쪽 가장자리인지 찾는 데 사용됩니다.

 function isScrolledIntoView(elem) { var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elemTop = $(elem).offset().top; return ((elemTop <= docViewBottom) && (elemTop >= docViewTop)); }

Snigdha Batra

WebResourcesDepot 은 얼마 전에 jQuery 를 사용하여 스크롤하는 동안 로드할 스크립트를 작성했습니다. 여기에서 라이브 데모를 볼 수 있습니다. 기능의 핵심은 이랬습니다.

 $(window).scroll(function(){ if ($(window).scrollTop() == $(document).height() - $(window).height()){ lastAddedLiveFunc(); } }); function lastAddedLiveFunc() { $('div#lastPostsLoader').html('<img src="images/bigLoader.gif">'); $.post("default.asp?action=getLastPosts&lastPostID="+$(".wrdLatest:last").attr("id"), function(data){ if (data != "") { $(".wrdLatest:last").after(data); } $('div#lastPostsLoader').empty(); }); };

Sampson

여기에 있는 대부분의 답변은 요소가 전체 페이지뿐만 아니라 div의 보기 밖으로 스크롤되기 때문에 요소가 숨겨질 수도 있다는 점을 고려하지 않습니다.

그 가능성을 다루기 위해 기본적으로 요소가 각 부모의 경계 안에 위치하는지 확인해야 합니다.

이 솔루션은 정확히 다음을 수행합니다.

 function(element, percentX, percentY){ var tolerance = 0.01; //needed because the rects returned by getBoundingClientRect provide the position up to 10 decimals if(percentX == null){ percentX = 100; } if(percentY == null){ percentY = 100; } var elementRect = element.getBoundingClientRect(); var parentRects = []; while(element.parentElement != null){ parentRects.push(element.parentElement.getBoundingClientRect()); element = element.parentElement; } var visibleInAllParents = parentRects.every(function(parentRect){ var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left); var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top); var visiblePercentageX = visiblePixelX / elementRect.width * 100; var visiblePercentageY = visiblePixelY / elementRect.height * 100; return visiblePercentageX + tolerance > percentX && visiblePercentageY + tolerance > percentY; }); return visibleInAllParents; };

또한 각 방향에서 볼 수 있는 백분율을 지정할 수 있습니다.
display: hidden 과 같은 다른 요인으로 인해 숨겨질 수 있는 가능성은 다루지 않습니다.

getBoundingClientRect 만 사용하기 때문에 모든 주요 브라우저에서 작동해야 합니다. 개인적으로 Chrome과 Internet Explorer 11에서 테스트했습니다.


Domysee

isScrolledIntoView 는 매우 필요한 기능이기 때문에 사용해 보았는데 뷰포트보다 높지 않은 요소에 대해서는 작동하지만 요소가 뷰포트만큼 크면 작동하지 않습니다. 이것을 쉽게 수정하려면 조건을 변경하십시오.

 return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));

이에:

 return (docViewBottom >= elemTop && docViewTop <= elemBottom);

여기에서 데모를 참조하십시오: http://jsfiddle.net/RRSmQ/


Robert

이것은 요소에 있는 패딩, 테두리 또는 여백과 뷰포트 자체보다 큰 요소를 고려합니다.

 function inViewport($ele) { var lBound = $(window).scrollTop(), uBound = lBound + $(window).height(), top = $ele.offset().top, bottom = top + $ele.outerHeight(true); return (top > lBound && top < uBound) || (bottom > lBound && bottom < uBound) || (lBound >= top && lBound <= bottom) || (uBound >= top && uBound <= bottom); }

그것을 호출하려면 다음과 같이 사용하십시오.

 var $myElement = $('#my-element'), canUserSeeIt = inViewport($myElement); console.log(canUserSeeIt); // true, if element is visible; false otherwise

Brent Barbata

다음은 또 다른 솔루션입니다.

 <script type="text/javascript"> $.fn.is_on_screen = function(){ var win = $(window); var viewport = { top : win.scrollTop(), left : win.scrollLeft() }; viewport.right = viewport.left + win.width(); viewport.bottom = viewport.top + win.height(); var bounds = this.offset(); bounds.right = bounds.left + this.outerWidth(); bounds.bottom = bounds.top + this.outerHeight(); return (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom)); }; if( $('.target').length > 0 ) { // if target element exists in DOM if( $('.target').is_on_screen() ) { // if target element is visible on screen after DOM loaded $('.log').html('<div class="alert alert-success">target element is visible on screen</div>'); // log info } else { $('.log').html('<div class="alert">target element is not visible on screen</div>'); // log info } } $(window).on('scroll', function(){ // bind window scroll event if( $('.target').length > 0 ) { // if target element exists in DOM if( $('.target').is_on_screen() ) { // if target element is visible on screen after DOM loaded $('.log').html('<div class="alert alert-success">target element is visible on screen</div>'); // log info } else { $('.log').html('<div class="alert">target element is not visible on screen</div>'); // log info } } }); </script>

JSFiddle 에서 보기


Adrian P.

function isScrolledIntoView(elem) { var docViewTop = $(window).scrollTop(), docViewBottom = docViewTop + $(window).height(), elemTop = $(elem).offset().top, elemBottom = elemTop + $(elem).height(); //Is more than half of the element visible return ((elemTop + ((elemBottom - elemTop)/2)) >= docViewTop && ((elemTop + ((elemBottom - elemTop)/2)) <= docViewBottom)); }

Pascal Gagneur

스크롤 가능한 DIV 컨테이너 내부 요소의 가시성을 확인해야 했습니다.

 //p = DIV container scrollable //e = element function visible_in_container(p, e) { var z = p.getBoundingClientRect(); var r = e.getBoundingClientRect(); // Check style visiblilty and off-limits return e.style.opacity > 0 && e.style.display !== 'none' && e.style.visibility !== 'hidden' && !(r.top > z.bottom || r.bottom < z.top || r.left > z.right || r.right < z.left); }

Pigmalión

이 훌륭한 답변 을 바탕으로 ES2015+를 사용하여 좀 더 단순화할 수 있습니다.

 function isScrolledIntoView(el) { const { top, bottom } = el.getBoundingClientRect() return top >= 0 && bottom <= window.innerHeight }

창 밖으로 나가는 상단에 신경 쓰지 않고 하단이 보이는 것만 신경 쓰면 다음과 같이 단순화 할 수 있습니다.

 function isSeen(el) { return el.getBoundingClientRect().bottom <= window.innerHeight }

또는 단일 라이너

 const isSeen = el => el.getBoundingClientRect().bottom <= window.innerHeight

rpearce

새로운 "inview" 이벤트를 추가하는 inview 라는 jQuery용 플러그인 이 있습니다.


다음은 이벤트를 사용하지 않는 jQuery 플러그인에 대한 몇 가지 코드입니다.

 $.extend($.expr[':'],{ inView: function(a) { var st = (document.documentElement.scrollTop || document.body.scrollTop), ot = $(a).offset().top, wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height(); return ot > st && ($(a).height() + ot) < (st + wh); } }); (function( $ ) { $.fn.inView = function() { var st = (document.documentElement.scrollTop || document.body.scrollTop), ot = $(this).offset().top, wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height(); return ot > st && ($(this).height() + ot) < (st + wh); }; })( jQuery );

나는 James라는 녀석 의 코멘트( http://remysharp.com/2009/01/26/element-in-view-event-plugin/ )에서 이것을 발견했습니다.


ness-EE

내 응용 프로그램에 그러한 방법이 있지만 jQuery를 사용하지 않습니다.

 /* Get the TOP position of a given element. */ function getPositionTop(element){ var offset = 0; while(element) { offset += element["offsetTop"]; element = element.offsetParent; } return offset; } /* Is a given element is visible or not? */ function isElementVisible(eltId) { var elt = document.getElementById(eltId); if (!elt) { // Element not found. return false; } // Get the top and bottom position of the given element. var posTop = getPositionTop(elt); var posBottom = posTop + elt.offsetHeight; // Get the top and bottom position of the *visible* part of the window. var visibleTop = document.body.scrollTop; var visibleBottom = visibleTop + document.documentElement.offsetHeight; return ((posBottom >= visibleTop) && (posTop <= visibleBottom)); }

편집: 이 방법은 IE(최소 버전 6)에서 잘 작동합니다. FF와의 호환성에 대한 설명을 읽으십시오.


Romain Linsolas

다른 div 내에서 항목을 스크롤하기 위해 이것을 조정하려면,

 function isScrolledIntoView (elem, divID) { var docViewTop = $('#' + divID).scrollTop(); var docViewBottom = docViewTop + $('#' + divID).height(); var elemTop = $(elem).offset().top; var elemBottom = elemTop + $(elem).height(); return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); }

Samiya Akhtar

스크롤할 때 요소가 현재 뷰포트에 있는지 확인하기 위해 jquery 플러그인 "onScreen"을 사용할 수 있습니다. 플러그인은 선택기가 화면에 나타날 때 선택기의 ":onScreen"을 true로 설정합니다. 이것은 프로젝트에 포함할 수 있는 플러그인에 대한 링크입니다. " http://benpickles.github.io/onScreen/jquery.onscreen.min.js "

나를 위해 작동하는 아래의 예를 시도해 볼 수 있습니다.

 $(document).scroll(function() { if($("#div2").is(':onScreen')) { console.log("Element appeared on Screen"); //do all your stuffs here when element is visible. } else { console.log("Element not on Screen"); //do all your stuffs here when element is not visible. } });

HTML 코드:

 <div id="div1" style="width: 400px; height: 1000px; padding-top: 20px; position: relative; top: 45px"></div> <br> <hr /> <br> <div id="div2" style="width: 400px; height: 200px"></div>

CSS:

 #div1 { background-color: red; } #div2 { background-color: green; }

Vasuki Dileep

요소가 75% 표시되는지 확인하기 위해 이 답변 을 기반으로 한 예입니다(즉, 25% 미만이 화면에서 벗어남).

 function isScrolledIntoView(el) { // check for 75% visible var percentVisible = 0.75; var elemTop = el.getBoundingClientRect().top; var elemBottom = el.getBoundingClientRect().bottom; var elemHeight = el.getBoundingClientRect().height; var overhang = elemHeight * (1 - percentVisible); var isVisible = (elemTop >= -overhang) && (elemBottom <= window.innerHeight + overhang); return isVisible; }

Brendan Nee

이 답변 의보다 효율적인 버전 :

 /** * Is element within visible region of a scrollable container * @param {HTMLElement} el - element to test * @returns {boolean} true if within visible region, otherwise false */ function isScrolledIntoView(el) { var rect = el.getBoundingClientRect(); return (rect.top >= 0) && (rect.bottom <= window.innerHeight); }

John Doherty

요소가 표시 속성을 "없음"이 아닌 다른 값으로 설정하여 품질을 볼 수 있도록 허용된 답변을 수정했습니다.

 function isScrolledIntoView(elem) { var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elemTop = $(elem).offset().top; var elemBottom = elemTop + $(elem).height(); var elemDisplayNotNone = $(elem).css("display") !== "none"; return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop) && elemDisplayNotNone); }

evanmcd

수평, 수직 또는 둘 다에서 Mootools를 사용하여 동일한 것을 달성하는 방법이 있습니다.

 Element.implement({ inVerticalView: function (full) { if (typeOf(full) === "null") { full = true; } if (this.getStyle('display') === 'none') { return false; } // Window Size and Scroll var windowScroll = window.getScroll(); var windowSize = window.getSize(); // Element Size and Scroll var elementPosition = this.getPosition(); var elementSize = this.getSize(); // Calculation Variables var docViewTop = windowScroll.y; var docViewBottom = docViewTop + windowSize.y; var elemTop = elementPosition.y; var elemBottom = elemTop + elementSize.y; if (full) { return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) && (elemBottom <= docViewBottom) && (elemTop >= docViewTop) ); } else { return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); } }, inHorizontalView: function(full) { if (typeOf(full) === "null") { full = true; } if (this.getStyle('display') === 'none') { return false; } // Window Size and Scroll var windowScroll = window.getScroll(); var windowSize = window.getSize(); // Element Size and Scroll var elementPosition = this.getPosition(); var elementSize = this.getSize(); // Calculation Variables var docViewLeft = windowScroll.x; var docViewRight = docViewLeft + windowSize.x; var elemLeft = elementPosition.x; var elemRight = elemLeft + elementSize.x; if (full) { return ((elemRight >= docViewLeft) && (elemLeft <= docViewRight) && (elemRight <= docViewRight) && (elemLeft >= docViewLeft) ); } else { return ((elemRight <= docViewRight) && (elemLeft >= docViewLeft)); } }, inView: function(full) { return this.inHorizontalView(full) && this.inVerticalView(full); }});

bmlkc

이 메서드는 요소의 일부가 페이지에 표시되는 경우 true를 반환합니다. 내 경우에는 더 잘 작동했으며 다른 사람을 도울 수 있습니다.

 function isOnScreen(element) { var elementOffsetTop = element.offset().top; var elementHeight = element.height(); var screenScrollTop = $(window).scrollTop(); var screenHeight = $(window).height(); var scrollIsAboveElement = elementOffsetTop + elementHeight - screenScrollTop >= 0; var elementIsVisibleOnScreen = screenScrollTop + screenHeight - elementOffsetTop >= 0; return scrollIsAboveElement && elementIsVisibleOnScreen; }

Rafael Garcia

나는 jQuery expr을 사용하는 것을 선호한다

 jQuery.extend(jQuery.expr[':'], { inview: function (elem) { var t = $(elem); var offset = t.offset(); var win = $(window); var winST = win.scrollTop(); var elHeight = t.outerHeight(true); if ( offset.top > winST - elHeight && offset.top < winST + elHeight + win.height()) { return true; } return false; } });

이 방법을 사용할 수 있도록

 $(".my-elem:inview"); //returns only element that is in view $(".my-elem").is(":inview"); //check if element is in view $(".my-elem:inview").length; //check how many elements are in view

scroll 이벤트 함수 등에 이러한 코드를 쉽게 추가하여 사용자가 보기를 스크롤할 때마다 확인할 수 있습니다.


Adam Pietrasiak

이 질문에 대한 답변이 30개가 넘는데 그 중 내가 사용하고 있는 놀랍도록 간단하고 순수한 JS 솔루션을 사용하는 사람은 아무도 없습니다. 다른 많은 사람들이 추진하고 있기 때문에 이 문제를 해결하기 위해 jQuery를 로드할 필요가 없습니다.

요소가 뷰포트 내에 있는지 확인하려면 먼저 본문 내의 요소 위치를 결정해야 합니다. 내가 생각했던 것처럼 우리는 이것을 재귀적으로 할 필요가 없습니다. element.getBoundingClientRect() 사용할 수 있습니다.

 pos = elem.getBoundingClientRect().top - document.body.getBoundingClientRect().top;

이 값은 오브젝트 상단과 본체 상단 간의 Y 차이입니다.

그런 다음 요소가 보기 내에 있는지 알려야 합니다. 대부분의 구현은 전체 요소가 뷰포트 내에 있는지 묻기 때문에 이것이 우리가 다룰 것입니다.

우선 창의 맨 위 위치는 window.scrollY 입니다.

창의 높이를 상단 위치에 추가하여 창의 하단 위치를 얻을 수 있습니다.

 var window_bottom_position = window.scrollY + window.innerHeight;

요소의 최상위 위치를 가져오는 간단한 함수를 만들 수 있습니다.

 function getElementWindowTop(elem){ return elem && typeof elem.getBoundingClientRect === 'function' ? elem.getBoundingClientRect().top - document.body.getBoundingClientRect().top : 0; }

이 함수는 창 내에서 요소의 맨 위 위치를 반환하거나 .getBoundingClientRect() 메서드를 사용하여 요소가 아닌 다른 것을 전달하면 0 이 방법은 오랫동안 사용되어 왔으므로 브라우저에서 지원하지 않는 것에 대해 걱정할 필요가 없습니다.

이제 요소의 최상위 위치는 다음과 같습니다.

 var element_top_position = getElementWindowTop(element);

또는 요소의 맨 아래 위치는 다음과 같습니다.

 var element_bottom_position = element_top_position + element.clientHeight;

이제 요소의 아래쪽 위치가 뷰포트의 위쪽 위치보다 낮은지 확인하고 요소의 위쪽 위치가 뷰포트의 아래쪽 위치보다 높은지 확인하여 요소가 뷰포트 내에 있는지 확인할 수 있습니다.

 if(element_bottom_position >= window.scrollY && element_top_position <= window_bottom_position){ //element is in view else //element is not in view

in-view 클래스를 추가하거나 제거하는 논리를 수행할 수 있으며 나중에 CSS에서 전환 효과로 처리할 수 있습니다.

다른 곳에서 이 솔루션을 찾지 못했다는 사실에 절대적으로 놀랐지만 이것이 가장 깨끗하고 효과적인 솔루션이며 jQuery를 로드할 필요가 없다고 믿습니다!


WebWanderer

내가 찾은 가장 쉬운 솔루션은 Intersection Observer API입니다 .

 var observer = new IntersectionObserver(function(entries) { if(entries[0].isIntersecting === true) console.log('Element has just become visible in screen'); }, { threshold: [0] }); observer.observe(document.querySelector("#main-container"));

Manash Kumar

스크롤 가능한 div(컨테이너)에 대한 간단한 수정

 var isScrolledIntoView = function(elem, container) { var containerHeight = $(container).height(); var elemTop = $(elem).position().top; var elemBottom = elemTop + $(elem).height(); return (elemBottom > 0 && elemTop < containerHeight); }

참고: 요소가 스크롤 가능한 div보다 큰 경우에는 작동하지 않습니다.


Derrick J Wippler

출처 : http:www.stackoverflow.com/questions/487073/how-to-check-if-element-is-visible-after-scrolling

반응형