use crate::core::ElementsMaybeSignal; use crate::{use_supported, watch}; use leptos::*; use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::prelude::*; use web_sys::MutationObserverInit; /// Reactive [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver). /// /// Watch for changes being made to the DOM tree. /// /// ## Demo /// /// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_mutation_observer) /// /// ## Usage /// /// ``` /// # use leptos::*; /// # use leptos::html::Pre; /// # use leptos_use::use_mutation_observer_with_options; /// # /// # #[component] /// # fn Demo(cx: Scope) -> impl IntoView { /// let el = create_node_ref::
(cx);
/// let (text, set_text) = create_signal(cx, "".to_string());
///
/// let mut init = web_sys::MutationObserverInit::new();
/// init.attributes(true);
///
/// use_mutation_observer_with_options(
///     cx,
///     el,
///     move |mutations, _| {
///         if let Some(mutation) = mutations.first() {
///             set_text.update(|text| *text = format!("{text}\n{:?}", mutation.attribute_name()));
///         }
///     },
///     init,
/// );
///
/// view! { cx,
///     
{ text }
/// } /// # } /// ``` pub fn use_mutation_observer( cx: Scope, target: El, callback: F, ) -> UseMutationObserverReturn where (Scope, El): Into>, T: Into + Clone + 'static, F: FnMut(Vec, web_sys::MutationObserver) + 'static, { use_mutation_observer_with_options(cx, target, callback, MutationObserverInit::default()) } /// Version of [`use_mutation_observer`] that takes a `web_sys::MutationObserverInit`. See [`use_mutation_observer`] for how to use. pub fn use_mutation_observer_with_options( cx: Scope, target: El, mut callback: F, options: web_sys::MutationObserverInit, ) -> UseMutationObserverReturn where (Scope, El): Into>, T: Into + Clone + 'static, F: FnMut(Vec, web_sys::MutationObserver) + 'static, { let closure_js = Closure::::new( move |entries: js_sys::Array, observer| { callback( entries .to_vec() .into_iter() .map(|v| v.unchecked_into::()) .collect(), observer, ); }, ) .into_js_value(); let observer: Rc>> = Rc::new(RefCell::new(None)); let is_supported = use_supported(cx, || JsValue::from("MutationObserver").js_in(&window())); let cleanup = { let observer = Rc::clone(&observer); move || { let mut observer = observer.borrow_mut(); if let Some(o) = observer.as_ref() { o.disconnect(); *observer = None; } } }; let targets = (cx, target).into(); let stop_watch = { let cleanup = cleanup.clone(); watch( cx, move || targets.get(), move |targets, _, _| { cleanup(); if is_supported.get() && !targets.is_empty() { let obs = web_sys::MutationObserver::new(closure_js.as_ref().unchecked_ref()) .expect("failed to create MutationObserver"); for target in targets.iter().flatten() { let target: web_sys::Element = target.clone().into(); let _ = obs.observe_with_options(&target, &options.clone()); } observer.replace(Some(obs)); } }, ) }; let stop = move || { cleanup(); stop_watch(); }; on_cleanup(cx, stop.clone()); UseMutationObserverReturn { is_supported, stop } } /// The return value of [`use_mutation_observer`]. pub struct UseMutationObserverReturn { /// Whether the browser supports the MutationObserver API pub is_supported: Signal, /// A function to stop and detach the MutationObserver pub stop: F, }