admin管理员组文章数量:1129454
I am building a toolbar that is going to be included into a page. the div
is going to be included in will default to display: none
.
Is there any way I can put an event listener on my toolbar to listen for when it becomes visible so it can initialize?
or will I have to pass it a variable from the containing page?
I am building a toolbar that is going to be included into a page. the div
is going to be included in will default to display: none
.
Is there any way I can put an event listener on my toolbar to listen for when it becomes visible so it can initialize?
or will I have to pass it a variable from the containing page?
- Does this answer help? stackoverflow.com/questions/1397251/… – kangax Commented Sep 23, 2009 at 3:46
- @kangax, thank you. But since its not widely implemented I think I'm going to scratch the whole event listener idea and go a different route. – JD Isaacks Commented Sep 23, 2009 at 12:51
- See this answer for an implementation of an "onVisible" event in JavaScript: stackoverflow.com/a/3807340/975097 – Anderson Green Commented Apr 3, 2013 at 2:33
- possible duplicate of How to implement an 'onVisible' event in Javascript? – user Commented Apr 8, 2014 at 6:31
- Could see this please. stackoverflow.com/questions/45429746/… – Varun Sharma Commented Aug 2, 2017 at 8:38
12 Answers
Reset to default 153Going forward, the new HTML Intersection Observer API is the thing you're looking for. It allows you to configure a callback that is called whenever one element, called the target, intersects either the device viewport or a specified element. It's available in latest versions of Chrome, Firefox and Edge. See https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API for more info.
Simple code example for observing display:none switching:
// Start observing visbility of element. On change, the
// the callback is called with Boolean visibility as
// argument:
function respondToVisibility(element, callback) {
var options = {
root: document.documentElement,
};
var observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
callback(entry.intersectionRatio > 0);
});
}, options);
observer.observe(element);
}
In action: https://jsfiddle.net/elmarj/u35tez5n/5/
var targetNode = document.getElementById('elementId');
var observer = new MutationObserver(function(){
if(targetNode.style.display != 'none'){
// doSomething
}
});
observer.observe(targetNode, { attributes: true, childList: true });
I might be a little late, but you could just use the MutationObserver to observe any changes on the desired element. If any change occurs, you'll just have to check if the element is displayed.
If you just want to run some code when an element becomes visible in the viewport:
function onVisible(element, callback) {
new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.intersectionRatio > 0) {
callback(element);
observer.disconnect();
}
});
}).observe(element);
if(!callback) return new Promise(r => callback=r);
}
When the element has become visible (event slightly) the intersection observer calls callback
and then destroys itself with .disconnect()
.
Use it like this:
onVisible(document.querySelector("#myElement"), () => console.log("it's visible"));
or like this:
await onVisible(document.querySelector("#myElement"));
console.log("it's visible");
If you want the callback to trigger when the element becomes fully visible then you should change entry.intersectionRatio > 0
to entry.intersectionRatio === 1
.
If the element is already visible when you call onVisible
, then the callback will fire immediately.
Related: If you want to immediately get a true
or false
for whether the element is currently visible, use this code (change the intersectionRatio
to suit your requirements).
There is at least one way, but it's not a very good one. You could just poll the element for changes like this:
var previous_style,
poll = window.setInterval(function()
{
var current_style = document.getElementById('target').style.display;
if (previous_style != current_style) {
alert('style changed');
window.clearInterval(poll);
} else {
previous_style = current_style;
}
}, 100);
The DOM standard also specifies mutation events, but I've never had the chance to use them, and I'm not sure how well they're supported. You'd use them like this:
target.addEventListener('DOMAttrModified', function()
{
if (e.attrName == 'style') {
alert('style changed');
}
}, false);
This code is off the top of my head, so I'm not sure if it'd work.
The best and easiest solution would be to have a callback in the function displaying your target.
I had this same problem and created a jQuery plugin to solve it for our site.
https://github.com/shaunbowe/jquery.visibilityChanged
Here is how you would use it based on your example:
$('#contentDiv').visibilityChanged(function(element, visible) {
alert("do something");
});
As @figha says, if this is your own web page, you should just run whatever you need to run after you make the element visible.
However, for the purposes of answering the question (and anybody making Chrome or Firefox Extensions, where this is a common use case), Mutation Summary and Mutation Observer will allow DOM changes to trigger events.
For example, triggering an event for a elements with data-widget
attribute being added to the DOM. Borrowing this excellent example from David Walsh's blog:
var observer = new MutationObserver(function(mutations) {
// For the sake of...observation...let's output the mutation to console to see how this all works
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
// Notify me of everything!
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};
// Node, config
// In this case we'll listen to all changes to body and child nodes
var targetNode = document.body;
observer.observe(targetNode, observerConfig);
Responses include added
, removed
, valueChanged
and more. valueChanged
includes all attributes, including display
etc.
A simple solution to this which works even for nested elements is to use the ResizeObserver.
It should work in all modern browsers (https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API).
When an element has css rule display: none
applied to it (whether directly or via an ancestor element) then all of its dimensions will be zero. So in order to detect becoming visible we just need an element with non-zero dimensions when visible.
const element = document.querySelector("#element");
const resizeWatcher = new ResizeObserver(entries => {
for (const entry of entries) {
console.log("Element", entry.target,
(entry.contentRect.width === 0) ?
"is now hidden" :
"is now visible"
)
}
});
resizeWatcher.observe(element)
Just to comment on the DOMAttrModified event listener browser support:
Cross-browser support
These events are not implemented consistently across different browsers, for example:
IE prior to version 9 didn't support the mutation events at all and does not implement some of them correctly in version 9 (for example, DOMNodeInserted)
WebKit doesn't support DOMAttrModified (see webkit bug 8191 and the workaround)
"mutation name events", i.e. DOMElementNameChanged and DOMAttributeNameChanged are not supported in Firefox (as of version 11), and probably in other browsers as well.
Source: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events
Expanding on Elmar's earlier answer, I used this to put focus on an input box in a Bootstrap navbar submenu.
I wanted the focus to go on the search box when the menu was expanded. .onfocus() wasn't working, I think because the element isn't visible at the time the event is triggered (even with the mouseup event). This worked perfectly though:
<ul class="navbar-nav ms-auto me-0 ps-3 ps-md-0">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" title="Search" id="navbardrop" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-search"></i>
</a>
<div class="dropdown-menu dropdown-menu-end search-menu">
<form action="{% url 'search' %}" method="get">
<div class="form-group row g-1 my-1 pb-1">
<div class="col">
<input type="text" name="query" id="searchbox" class="form-control py-1 ps-2" value="{% if search_query %}{{ search_query }}{% endif %}">
</div>
<div class="col-auto">
<input type="submit" value="Search" class="btn-primary form-control py-1">
</div>
</div>
</form>
</div>
</li>
</ul>
Then in the js:
respondToVisibility = function (element, callback) {
var options = {
root: document.documentElement,
};
var observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
callback(entry.intersectionRatio > 0);
});
}, options);
observer.observe(element);
};
respondToVisibility(document.getElementById("searchbox"), (visible) => {
if (visible) {
document.getElementById("searchbox").focus();
}
});
Here is a hook I created for use with React.
'use client'
import { useEffect } from "react";
export default function useVisibility(elementId, onVisible) {
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
const entry = entries[0];
if(entry.isIntersecting) {
onVisible()
}
});
const element = document.getElementById(elementId);
if (element) {
observer.observe(element);
}
return () => {
if (element) {
observer.unobserve(element);
}
};
}, []);
}
Here is an example of how to use it
...
export default function MyComponent() {
useVisibility('my-element', () => console.log("I see you!"))
return <p id='my-element' styles={{marginTop: '1000px'}}>Here I go!</p>
}
Javascript events deal with User Interaction, if your code is organised enough you should be able to call the initialising function in the same place where the visibility changes (i.e. you shouldn't change myElement.style.display
on many places, instead, call a function/method that does this and anything else you might want).
my solution:
; (function ($) {
$.each([ "toggle", "show", "hide" ], function( i, name ) {
var cssFn = $.fn[ name ];
$.fn[ name ] = function( speed, easing, callback ) {
if(speed == null || typeof speed === "boolean"){
var ret=cssFn.apply( this, arguments )
$.fn.triggerVisibleEvent.apply(this,arguments)
return ret
}else{
var that=this
var new_callback=function(){
callback.call(this)
$.fn.triggerVisibleEvent.apply(that,arguments)
}
var ret=this.animate( genFx( name, true ), speed, easing, new_callback )
return ret
}
};
});
$.fn.triggerVisibleEvent=function(){
this.each(function(){
if($(this).is(':visible')){
$(this).trigger('visible')
$(this).find('[data-trigger-visible-event]').triggerVisibleEvent()
}
})
}
})(jQuery);
for example:
if(!$info_center.is(':visible')){
$info_center.attr('data-trigger-visible-event','true').one('visible',processMoreLessButton)
}else{
processMoreLessButton()
}
function processMoreLessButton(){
//some logic
}
本文标签: javascriptEvent listener for when element becomes visibleStack Overflow
版权声明:本文标题:javascript - Event listener for when element becomes visible? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736740983a1950507.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论