【问题标题】:How to wait until an element exists with JavaScript?JavaScript 如何等到元素存在?
【发布时间】:2019-12-14 22:19:21
【问题描述】:

我正在使用proxy object,在其中我检测到对象值更改,然后通过 AJAX 加载新内容,我使用setInterval 函数等待 AJAX 请求中的元素存在,然后执行一段代码。我这样做是因为我的情况需要它。我做了一个简短的 sn-p 示例:

var handler = {
    makeThings: 0,
    otherStuff: 0
};
var globalHandler = new Proxy(handler, {
    set: function(obj, prop, value) {
        obj[prop] = value
        if (prop == "makeThings") {
            var clearTimeSearchProxy = setInterval(function() {
                if ($("p").length) {
                    console.log("The element finally exist and we execute code");
                    clearTimeout(clearTimeSearchProxy);
                }
            }, 100);
        }
        return true;
    }
});

$(document).ready(function() {
    $("button").on("click", function() {
        globalHandler.makeThings = 1;
        //This element comes with ajax but I use a setTimeout for this example
        setTimeout(function() {
            $("#newContent").append("<p>Ajax element</p>");
        }, 2000);
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
  <button>New content</button>
  <div id="newContent"></div>
</body>

现在我想知道如何以更清洁、高效和优雅的方式改进代码。当来自 AJAX 的元素存在于 DOM 中时,我正在考虑使用 promises 而不是 setInterval 来执行代码。

我怎样才能让它工作?我应该在这种情况下使用其他 JavaScript 功能而不是 promises 吗?我坚持实现我所需要的承诺,这是我迄今为止所尝试的。

var handler = {
    makeThings: 0,
    otherStuff: 0
};
var globalHandler = new Proxy(handler, {
    set: function(obj, prop, value) {
        obj[prop] = value
        if (prop == "makeThings") {
            var myFirstPromise = new Promise((resolve, reject) => {
                if ($("p").length) {
                    resolve("Exist");
                } else {
                    reject("It doesnt exist.");
                }
            });

            myFirstPromise.then((data) => {
                console.log("Done " + data);
            }).catch((reason) => {
                console.log("Handle rejected promise: " + reason);
            });
        }
        return true;
    }
});

$(document).ready(function() {
    $("button").on("click", function() {
        globalHandler.makeThings = 1;
        //This element comes with ajax but I use a setTimeout for this example
        setTimeout(function() {
            $("#newContent").append("<p>Ajax element</p>");
        }, 2000);
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
  <button>New content</button>
  <div id="newContent"></div>
</body>

【问题讨论】:

标签: javascript jquery ecmascript-6 promise es6-promise


【解决方案1】:

rxjs 可以高度简化您尝试做的事情。一个非常基本的实现,仅使用主题和订阅:

const {
  Subject
} = rxjs;

const sub = new Subject();

sub.subscribe(e => {
  console.log(`received data ${e}`);
  // do your thing
});

// simulate something async
setTimeout(() => {
  sub.next('foo');
}, 1000);
&lt;script src="https://unpkg.com/rxjs@6.5.2/bundles/rxjs.umd.min.js"&gt;&lt;/script&gt;

【讨论】:

    【解决方案2】:

    不要等待。而是订阅目标元素更改的通知。

    用于监听 DOM 树变化的 API 是 MutationObserver

    MutationObserver 接口提供了监视对 DOM 树所做更改的能力。它旨在替代旧的 Mutation Events 功能,该功能是 DOM3 事件规范的一部分。

    用它来观察元素的变化如下:

    // You selected `$("p")` in your snippet, suggesting you're watching for the inclusion of 'any' `p` element.
    // Therefore we'll watch the `body` element in this example
    const targetNode = document.body;
    
    // Options for the observer (which mutations to observe)
    const config = {
        attributes: false,
        characterData: false,
        childList: true,
        subtree: true
    };
    
    // Callback function to execute when mutations are observed
    const callback = function(mutationsList, observer) {
        for(let mutation of mutationsList) {
    
            if ( mutation.type === "childList" ) {
                continue;
            }
    
            const addedNodes = Array.from( mutation.addedNodes) ;
    
            if ( addedNodes && addedNodes.some( node => node.nodeName === "P" ) ) {
                observer.disconnect();
    
                console.log("The element finally exist and we execute code");
            }
        }
    };
    
    // Create an observer instance linked to the callback function
    const observer = new MutationObserver(callback);
    
    // Start observing the target node for configured mutations
    observer.observe(targetNode, config);
    

    【讨论】:

    • 是否需要在配置中将 'childList' 设置为 true,然后在观察者回调中跳过该突变类型?
    • 如果您确实确信 config 是为观察子添加/删除而严格设置的,那么您不应该检查突变类型。
    【解决方案3】:

    我终于用MutationObserverinterface而不是promises轻松做到了。

    var handler = {
        makeThings: 0,
        otherStuff: 0
    };
    var globalHandler = new Proxy(handler, {
        set: function(obj, prop, value) {
            obj[prop] = value
            if (prop == "makeThings") {
                var observer = new MutationObserver(function(mutations) {
                    if ($("p").length) {
                        console.log("Exist, lets do something");
                        observer.disconnect();
                    }
                });
                // start observing
                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
            }
            return true;
        }
    });
    
    $(document).ready(function() {
        $("button").on("click", function() {
            $("p").remove();
            globalHandler.makeThings = 1;
            //This element comes with ajax but I use a setTimeout for this example
            setTimeout(function() {
                $("#newContent").append("<p>Ajax element</p>");
            }, 2000);
        });
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <body>
      <button>New content</button>
      <div id="newContent"></div>
    </body>

    【讨论】:

      猜你喜欢
      • 2018-10-23
      • 2020-04-25
      • 2014-01-21
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      • 2013-04-15
      相关资源
      最近更新 更多