使函数等待直到元素存在

IT技术 javascript jquery html function
2021-01-30 20:00:52

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

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;
    }
6个回答

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

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

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

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

在将其他东西放入其中之前等待 Ajax 创建某些东西的绝佳解决方案。非常感谢。
2021-03-17 20:00:52
@iftah 如果选择器是变量,我将如何使其工作?此外,如果它是 ID 或类选择器也会更改。有时当我选择一个类时会返回多个元素,我需要找到一种方法将索引传递给选择器以找出哪个元素。我该怎么做?谢谢
2021-03-19 20:00:52
在使用给定的解决方案时,还有一件事很重要,您应该在 for 循环中包含这段代码并设置最大重试计数器,如果出现问题,您不会以无限循环结束:)
2021-03-22 20:00:52
用于 angularjs typeahead 的完美解决方案。谢谢你指引我正确的方向!
2021-04-06 20:00:52
@Kraglon 这是一个完全不同的问题,不适合对此答案发表评论。我建议你问一个新问题,解释你尝试了什么,问题是什么等等......
2021-04-12 20:00:52

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

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

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

// 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
});

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

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

喜欢这个。谢谢你。
2021-04-01 20:00:52
这种模式在很多情况下都非常有用,尤其是当您将 JS 拉入页面并且不知道是否加载了其他项目时。
2021-04-02 20:00:52
最佳答案!谢谢!
2021-04-02 20:00:52
这真太了不起了!我希望我早点知道这存在。
2021-04-04 20:00:52
我一直使用旧浏览器 (ff38),这救了我。
2021-04-06 20:00:52

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

ES5

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);
    }
}

ES6

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

用法

checkElement('body') //use whichever selector you want
.then((element) => {
     console.info(element);
     //Do whatever you want now the element is there
});
有错误。使用生成器函数时,应在每个循环中更新 querySelector:while (document.querySelector(selector) === null) {await rafAsync()}
2021-03-26 20:00:52
请问您为什么要为选择器创建这么多变量分配?这更好 afaik 的原因是它会更快,然后每次更改动画帧时都必须检查选择器。
2021-03-31 20:00:52

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

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

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

@DanielMöller 您可能需要查看Promises以更好地理解此代码。基本上,代码在这里所做的是设置 500 毫秒的超时并等待它完成,然后再启动 while 循环的新迭代。巧妙解决!
2021-03-24 20:00:52
是什么r呢?
2021-04-07 20:00:52
这很整洁!
2021-04-08 20:00:52
好吧,好吧,但它从哪里来?它有什么作用?你要送setTimeout什么?
2021-04-12 20:00:52

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

const checkElement = async selector => {
  while ( document.querySelector(selector) === null) {
    await new Promise( resolve =>  requestAnimationFrame(resolve) )
  }
  return document.querySelector(selector); 
};

使用:

checkElement('.myElement').then((selector) => {
  console.log(selector);
});
恕我直言,这比其他答案更好,并使用比setInterval. +1
2021-03-16 20:00:52
很好的答案。最少的代码行。这个答案不会看时钟并大大减慢页面速度 - 相反,它使用requestAnimationFrame. 现在编辑以展示如何使用它。
2021-04-03 20:00:52