jQuery 使函数等待元素存在

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16149431/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 16:32:49  来源:igfitidea点击:

Make function wait until element exists

javascriptjqueryhtmlfunction

提问by Steven

I'm trying to add a canvas over another canvas – how can I make this function wait to start until the first canvas is created?

我正在尝试在另一个画布上添加一个画布——我怎样才能让这个函数等到第一个画布创建后才开始?

function PaintObject(brush) {

    this.started = false;

    // get handle of the main canvas, as a DOM object, not as a jQuery Object. Context is unfortunately not yet
    // available in jquery canvas wrapper object.
    var mainCanvas = $("#" + brush).get(0);

    // Check if everything is ok
    if (!mainCanvas) {alert("canvas undefined, does not seem to be supported by your browser");}
    if (!mainCanvas.getContext) {alert('Error: canvas.getContext() undefined !');}

    // Get the context for drawing in the canvas
    var mainContext = mainCanvas.getContext('2d');
    if (!mainContext) {alert("could not get the context for the main canvas");}

    this.getMainCanvas = function () {
        return mainCanvas;
    }
    this.getMainContext = function () {
        return mainContext;
    }

    // Prepare a second canvas on top of the previous one, kind of second "layer" that we will use
    // in order to draw elastic objects like a line, a rectangle or an ellipse we adjust using the mouse
    // and that follows mouse movements
    var frontCanvas = document.createElement('canvas');
    frontCanvas.id = 'canvasFront';
    // Add the temporary canvas as a second child of the mainCanvas parent.
    mainCanvas.parentNode.appendChild(frontCanvas);

    if (!frontCanvas) {
        alert("frontCanvas null");
    }
    if (!frontCanvas.getContext) {
        alert('Error: no frontCanvas.getContext!');
    }
    var frontContext = frontCanvas.getContext('2d');
    if (!frontContext) {
        alert("no TempContext null");
    }

    this.getFrontCanvas = function () {
        return frontCanvas;
    }
    this.getFrontContext = function () {
        return frontContext;
    }

回答by Iftah

If you have access to the code that creates the canvas - simply call the function right there after the canvas is created.

如果您有权访问创建画布的代码 - 只需在创建画布后立即调用该函数即可。

If you have no access to that code (eg. If it is a 3rd party code such as google maps) then what you could do is test for the existence in an interval:

如果您无法访问该代码(例如,如果它是 3rd 方代码,例如谷歌地图),那么您可以做的是在一个时间间隔内测试是否存在:

var checkExist = setInterval(function() {
   if ($('#the-canvas').length) {
      console.log("Exists!");
      clearInterval(checkExist);
   }
}, 100); // check every 100ms

But note - many times 3rd party code has an option to activate your code (by callback or event triggering) when it finishes to load. That may be where you can put your function. The interval solution is really a bad solution and should be used only if nothing else works.

但请注意 - 很多时候 3rd 方代码可以选择在完成加载时激活您的代码(通过回调或事件触发)。那可能是您可以放置​​函数的地方。间隔解决方案确实是一个糟糕的解决方案,只有在其他方法都不起作用时才应该使用它。

回答by damd

Depending on which browser you need to support, there's the option of MutationObserver.

根据您需要支持的浏览器,有MutationObserver选项。

EDIT: All major browsers support MutationObserver now.

编辑:所有主要浏览器现在都支持 MutationObserver

Something along the lines of this should do the trick:

与此类似的事情应该可以解决问题:

// callback executed when canvas was found
function handleCanvas(canvas) { ... }

// set up the mutation observer
var observer = new MutationObserver(function (mutations, me) {
  // `mutations` is an array of mutations that occurred
  // `me` is the MutationObserver instance
  var canvas = document.getElementById('my-canvas');
  if (canvas) {
    handleCanvas(canvas);
    me.disconnect(); // stop observing
    return;
  }
});

// start observing
observer.observe(document, {
  childList: true,
  subtree: true
});

N.B. I haven't tested this code myself, but that's the general idea.

注意我自己没有测试过这段代码,但这是一般的想法。

You can easily extend this to only search the part of the DOM that changed. For that, use the mutationsargument, it's an array of MutationRecordobjects.

您可以轻松地将其扩展为仅搜索更改的 DOM 部分。为此,使用mutations参数,它是一个MutationRecord对象数组。

回答by Jamie Hutber

This will only work with modern browsers but I find it easier to just use a thenso please test first but:

这仅适用于现代浏览器,但我发现使用 a 更容易,then所以请先测试,但是:

Code

代码

function rafAsync() {
    return new Promise(resolve => {
        requestAnimationFrame(resolve); //faster than set time out
    });
}

function checkElement(selector) {
    if (document.querySelector(selector) === null) {
        return rafAsync().then(() => checkElement(selector));
    } else {
        return Promise.resolve(true);
    }
}

Or using generator functions

或者使用生成器函数

async function checkElement(selector) {
    const querySelector = document.querySelector(selector);
    while (querySelector === null) {
        await rafAsync()
    }
    return querySelector;
}  

Usage

用法

checkElement('body') //use whichever selector you want
.then((element) => {
     console.info(element);
     //Do whatever you want now the element is there
});

回答by Jamie Hutber

A more modern approach to waiting for elements:

一种更现代的等待元素的方法:

while(!document.querySelector(".my-selector")) {
  await new Promise(r => setTimeout(r, 500));
}
// now the element is loaded

Note that this code would need to be wrapped in an async function.

请注意,此代码需要包含在异步函数中

回答by wLc

Here's a minor improvement over Jamie Hutber's answer

这是杰米哈特伯的回答的一个小改进

const checkElement = async selector => {

while ( document.querySelector(selector) === null) {
    await new Promise( resolve =>  requestAnimationFrame(resolve) )
}

return document.querySelector(selector); };

回答by ncubica

Is better to relay in requestAnimationFramethan in a setTimeout. this is my solution in es6 modules and using Promises.

中继requestAnimationFrame比在setTimeout. 这是我在 es6 模块中的解决方案并使用Promises.

es6, modules and promises:

es6,模块和承诺:

// onElementReady.js
const onElementReady = $element => (
  new Promise((resolve) => {
    const waitForElement = () => {
      if ($element) {
        resolve($element);
      } else {
        window.requestAnimationFrame(waitForElement);
      }
    };
    waitForElement();
  })
);

export default onElementReady;

// in your app
import onElementReady from './onElementReady';

const $someElement = document.querySelector('.some-className');
onElementReady($someElement)
  .then(() => {
    // your element is ready
  }

plain js and promises:

plain js and promises

var onElementReady = function($element) {
  return new Promise((resolve) => {
    var waitForElement = function() {
      if ($element) {
        resolve($element);
      } else {
        window.requestAnimationFrame(waitForElement);
      }
    };
    waitForElement();
  })
};

var $someElement = document.querySelector('.some-className');
onElementReady($someElement)
  .then(() => {
    // your element is ready
  });

回答by FrankL

Here is a solution using observables.

这是一个使用 observables 的解决方案。

waitForElementToAppear(elementId) {                                          

    return Observable.create(function(observer) {                            
            var el_ref;                                                      
            var f = () => {                                                  
                el_ref = document.getElementById(elementId);                 
                if (el_ref) {                                                
                    observer.next(el_ref);                                   
                    observer.complete();                                     
                    return;                                                  
                }                                                            
                window.requestAnimationFrame(f);                             
            };                                                               
            f();                                                             
        });                                                                  
}                                                                            

Now you can write

现在你可以写

waitForElementToAppear(elementId).subscribe(el_ref => doSomethingWith(el_ref);

回答by Carmela

You can check if the dom already exists by setting a timeout until it is already rendered in the dom.

您可以通过设置超时来检查 dom 是否已经存在,直到它已经在 dom 中呈现。

var panelMainWrapper = document.getElementById('panelMainWrapper');
setTimeout(function waitPanelMainWrapper() {
    if (document.body.contains(panelMainWrapper)) {
        $("#panelMainWrapper").html(data).fadeIn("fast");
    } else {
        setTimeout(waitPanelMainWrapper, 10);
    }
}, 10);

回答by heriberto perez

Another variation of Iftah

Iftah的另一种变体

var counter = 10;
var checkExist = setInterval(function() {
  console.log(counter);
  counter--
  if ($('#the-canvas').length || counter === 0) {
    console.log("by bye!");
    clearInterval(checkExist);
  }
}, 200);

Just in case the element is never shown, so we don't check infinitely.

以防万一元素从未显示,所以我们不会无限地检查。

回答by rdhainaut

If you want a generic solution using MutationObserver you can use this function

如果您想要使用 MutationObserver 的通用解决方案,您可以使用此功能

// MIT Licensed
// Author: jwilson8767

/**
 * Waits for an element satisfying selector to exist, then resolves promise with the element.
 * Useful for resolving race conditions.
 *
 * @param selector
 * @returns {Promise}
 */
export function elementReady(selector) {
  return new Promise((resolve, reject) => {
    const el = document.querySelector(selector);
    if (el) {resolve(el);}
    new MutationObserver((mutationRecords, observer) => {
      // Query for elements matching the specified selector
      Array.from(document.querySelectorAll(selector)).forEach((element) => {
        resolve(element);
        //Once we have resolved we don't need the observer anymore.
        observer.disconnect();
      });
    })
      .observe(document.documentElement, {
        childList: true,
        subtree: true
      });
  });
}

Source: https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
Example how to use it

来源:https: //gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
使用示例

elementReady('#someWidget').then((someWidget)=>{someWidget.remove();});

Note: MutationObserver has a great browser support; https://caniuse.com/#feat=mutationobserver

注意:MutationObserver 有很好的浏览器支持;https://caniuse.com/#feat=mutationobserver

Et voilà ! :)

等等!:)