web-dev-qa-db-ja.com

要素が存在するまで関数を待機させる

別のキャンバスの上にキャンバスを追加しようとしています-最初のキャンバスが作成されるまで、この関数を開始まで待機させるにはどうすればよいですか?

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;
    }
105
Steven

キャンバスを作成するコードにアクセスできる場合は、キャンバスが作成された直後に関数を呼び出してください。

そのコードにアクセスできない場合(たとえば、Googleマップなどのサードパーティのコードの場合)、間隔内での存在をテストすることができます。

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

しかし、注意してください-サードパーティのコードには、ロードの完了時にコードをアクティブにするオプションがあります(コールバックまたはイベントトリガーによって)。それはあなたの関数を置くことができる場所かもしれません。間隔ソリューションは実際には悪いソリューションであり、他に何も機能しない場合にのみ使用する必要があります。

210
Iftah

サポートする必要のあるブラウザに応じて、 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
});

N.B.私はこのコードを自分でテストしていませんが、それは一般的な考え方です。

これを簡単に拡張して、変更されたDOMの一部のみを検索できます。そのためには、mutations引数を使用します。これは MutationRecord オブジェクトの配列です。

24
damd

これは最新のブラウザでのみ機能しますが、thenを使用する方が簡単だと思うので、最初にテストしてください:

コード

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

またはジェネレーター関数を使用

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

使用法

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

要素を待つためのより現代的なアプローチ:

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

このコードは async function でラップする必要があることに注意してください。

16
user993683

requestAnimationFrameよりもsetTimeoutでリレーする方が適切です。これはes6モジュールでの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

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

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);
3
Carmela

Jamie Hutberの答えを少し改善したものです

const checkElement = async selector => {

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

return document.querySelector(selector); };
3
wLc

非常に簡単です-キャンバスを作成するまでこの関数を呼び出さないでください(つまり、clickハンドラー内で呼び出します)。

0
Alnitak