All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dominokit.domino.ui.utils.BodyObserver Maven / Gradle / Ivy

There is a newer version: 2.0.4
Show newest version
/*
 * Copyright © 2019 Dominokit
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dominokit.domino.ui.utils;

import static elemental2.dom.DomGlobal.document;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

import elemental2.core.JsArray;
import elemental2.dom.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import jsinterop.base.Js;
import org.jboss.elemento.Id;
import org.jboss.elemento.ObserverCallback;

/**
 * This class allows us to listen to the elements attach/detach life cycle
 *
 * 

the class will register a {@link MutationObserver} on the document body element only and uses * query selectors to get attached/detached elements from the dom * *

When we register an attach/detach listener on an element we assign it a special id attribute * on-attach-uid/on-detach-uid to help locating elements from the {@link * MutationRecord}s */ final class BodyObserver { private static String ATTACH_UID_KEY = "on-attach-uid"; private static String DETACH_UID_KEY = "on-detach-uid"; private static List detachObservers = new ArrayList<>(); private static List attachObservers = new ArrayList<>(); private static boolean ready = false; private BodyObserver() {} private static void startObserving() { MutationObserver mutationObserver = new MutationObserver( (JsArray records, MutationObserver observer) -> { MutationRecord[] recordsArray = Js.uncheckedCast(records.asArray(new MutationRecord[records.length])); for (MutationRecord record : recordsArray) { if (!record.removedNodes.asList().isEmpty()) { onElementsRemoved(record); } if (!record.addedNodes.asList().isEmpty()) { onElementsAppended(record); } } return null; }); MutationObserverInit mutationObserverInit = MutationObserverInit.create(); mutationObserverInit.setChildList(true); mutationObserverInit.setSubtree(true); mutationObserver.observe(document.body, mutationObserverInit); ready = true; } private static void onElementsAppended(MutationRecord record) { List nodes = record.addedNodes.asList(); List observed = new ArrayList<>(); for (ElementObserver elementObserver : attachObservers) { if (isNull(elementObserver.observedElement())) { observed.add(elementObserver); } else { if (nodes.contains(elementObserver.observedElement()) || isChildOfAddedNode(record, elementObserver.attachId())) { elementObserver.callback().onObserved(record); observed.add(elementObserver); } } } attachObservers.removeAll(observed); } private static boolean isChildOfAddedNode(MutationRecord record, String attachId) { return isChildOfObservedNode(attachId, record.addedNodes.asList(), ATTACH_UID_KEY); } private static void onElementsRemoved(MutationRecord record) { List nodes = record.removedNodes.asList(); List observed = new ArrayList<>(); for (ElementObserver elementObserver : detachObservers) { if (isNull(elementObserver.observedElement())) { observed.add(elementObserver); } else { if (nodes.contains(elementObserver.observedElement()) || isChildOfRemovedNode(record, elementObserver.attachId())) { elementObserver.callback().onObserved(record); observed.add(elementObserver); } } } detachObservers.removeAll(observed); } private static boolean isChildOfRemovedNode(MutationRecord record, final String detachId) { return isChildOfObservedNode(detachId, record.removedNodes.asList(), DETACH_UID_KEY); } private static boolean isChildOfObservedNode( String attachId, List nodes, String attachUidKey) { for (int i = 0; i < nodes.size(); i++) { Node elementNode = Js.uncheckedCast(nodes.get(i)); if (Node.ELEMENT_NODE == elementNode.nodeType) { if (nonNull(elementNode.querySelector("[" + attachUidKey + "='" + attachId + "']"))) { return true; } } } return false; } /** * Check if the observer is already started, if not it will start it, then register and callback * for when the element is attached to the dom. * * @param element the {@link HTMLElement} we want to know when it get attached to the DOM * @param callback {@link ObserverCallback} to be called when the element is attached to the DOM * @return the {@link ElementObserver} that has attach information of the element and also can be * used to remove the listener */ static ElementObserver addAttachObserver(HTMLElement element, ObserverCallback callback) { if (!ready) { startObserving(); } ElementObserver observer = createObserver( element, callback, ATTACH_UID_KEY, elementObserver -> attachObservers.remove(elementObserver)); attachObservers.add(observer); return observer; } /** * Check if the observer is already started, if not it will start it, then register and callback * for when the element is removed from the dom. * * @param element the {@link HTMLElement} we want to know when it get detached from the DOM * @param callback {@link ObserverCallback} to be called when the element is detached from the DOM * @return the {@link ElementObserver} that has detach information of the element and also can be * used to remove the listener */ static ElementObserver addDetachObserver(HTMLElement element, ObserverCallback callback) { if (!ready) { startObserving(); } ElementObserver observer = createObserver( element, callback, DETACH_UID_KEY, elementObserver -> detachObservers.remove(elementObserver)); detachObservers.add(observer); return observer; } private static ElementObserver createObserver( HTMLElement element, ObserverCallback callback, String idAttributeName, Consumer onRemoveHandler) { String elementId = element.getAttribute(idAttributeName); if (elementId == null) { element.setAttribute(idAttributeName, Id.unique()); } return new ElementObserver() { @Override public String attachId() { return element.getAttribute(idAttributeName); } @Override public HTMLElement observedElement() { return element; } @Override public ObserverCallback callback() { return callback; } @Override public void remove() { onRemoveHandler.accept(this); } }; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy