web-dev-qa-db-ger.com

Funktion warten lassen, bis Element existiert

Ich versuche, eine Leinwand über einer anderen Leinwand hinzuzufügen. Wie kann ich diese Funktion warten lassen, bis die erste Leinwand erstellt ist?

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

Wenn Sie Zugriff auf den Code haben, mit dem die Leinwand erstellt wird, rufen Sie einfach die Funktion direkt nach dem Erstellen der Leinwand auf.

Wenn Sie keinen Zugriff auf diesen Code haben (z. B. ein Drittanbieter-Code wie Google Maps), können Sie in einem Intervall die Existenz prüfen:

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

Beachten Sie jedoch, dass Drittanbieter-Code oft die Option hat, Ihren Code zu aktivieren (durch Rückruf oder Ereignisauslösung), wenn der Ladevorgang abgeschlossen ist. Hier können Sie Ihre Funktion einsetzen. Die Intervalllösung ist wirklich eine schlechte Lösung und sollte nur verwendet werden, wenn nichts anderes funktioniert.

196
Iftah

Je nachdem, welchen Browser Sie unterstützen müssen, gibt es die Option MutationObserver .

Etwas in dieser Richtung sollte den Trick tun:

// 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. Ich habe diesen Code nicht selbst getestet, aber das ist die allgemeine Idee.

Sie können dies einfach erweitern, um nur den geänderten Teil des DOM zu durchsuchen. Verwenden Sie dazu das Argument mutations, ein Array von MutationRecord objects.

22
damd

Dies funktioniert nur mit modernen Browsern, aber ich finde es einfacher, einfach eine then zu verwenden.

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

Oder mit Generatorfunktionen

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

Verwendungszweck

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

Ein moderner Ansatz für das Warten auf Elemente:

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

Beachten Sie, dass dieser Code in eine async-Funktion eingeschlossen werden muss.

12
user993683

Ist besser in requestAnimationFrame weiterzuleiten als in einer setTimeout. Dies ist meine Lösung in ES6-Modulen und mit Promises.

es6, Module und Versprechen:

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

Sie können überprüfen, ob der Dom bereits existiert, indem Sie ein Timeout festlegen, bis er bereits im Dom gerendert ist.

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

Es ist sehr einfach - rufen Sie diese Funktion nur auf, wenn Sie die Leinwand bereits erstellt haben (d. H. Innerhalb des click-Handlers).

0
Alnitak

Hier ist eine kleine Verbesserung gegenüber der Antwort von Jamie Hutber

const checkElement = async selector => {

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

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