Buat fungsi menunggu hingga elemen ada

159

Saya mencoba menambahkan kanvas di atas kanvas lain - bagaimana saya bisa membuat fungsi ini menunggu untuk memulai sampai kanvas pertama dibuat?

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;
    }
Steven
sumber
4
Saat Anda membuat kanvas saat klik, jalankan fungsi atau picu peristiwa yang menjalankan penangan yang menjalankan fungsi. tidak ada acara lintas-browser bawaan yang terjadi ketika suatu elemen tersedia.
Kevin B
kemungkinan duplikat dari Bagaimana menunggu sampai ada elemen?
user2284570

Jawaban:

305

Jika Anda memiliki akses ke kode yang menciptakan kanvas - cukup panggil fungsi di sana setelah kanvas dibuat.

Jika Anda tidak memiliki akses ke kode itu (mis. Jika kode pihak ke-3 seperti google maps) maka yang dapat Anda lakukan adalah menguji keberadaannya dalam interval:

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

Tetapi perhatikan - berkali-kali kode pihak ke-3 memiliki opsi untuk mengaktifkan kode Anda (dengan panggilan balik atau pemicu acara) ketika selesai memuat. Itu mungkin di mana Anda dapat menempatkan fungsi Anda. Solusi interval benar-benar solusi yang buruk dan harus digunakan hanya jika tidak ada yang berfungsi.

Iftah
sumber
solusi sempurna untuk digunakan pada angularjs typeahead. Terima kasih telah membimbing saya ke arah yang benar!
JuanTrev
1
Solusi terbaik untuk menunggu sesuatu diciptakan oleh Ajax sebelum meletakkan beberapa hal lain di sana. Terima kasih banyak.
Countzero
@iftah Bagaimana saya bisa membuatnya bekerja jika pemilihnya adalah variabel? Juga, jika itu adalah perubahan ID atau pemilih Kelas juga. Kadang-kadang ada beberapa elemen yang dikembalikan ketika saya memilih dengan kelas, dan saya perlu menemukan cara untuk meneruskan indeks ke pemilih untuk mencari tahu yang mana. Bagaimana saya melakukan ini? Terima kasih
Kragalon
@ Kallon ini adalah pertanyaan yang sama sekali berbeda dan tidak cocok untuk komentar dari jawaban ini. Saya sarankan Anda mengajukan pertanyaan baru, jelaskan apa yang Anda coba, apa masalahnya, dll ...
Iftah
8
Satu hal lagi yang penting untuk disebutkan ketika menggunakan solusi yang diberikan, Anda harus memiliki sepotong kode di dalam for loop dan mengatur counter coba lagi maksimum, jika ada kesalahan Anda tidak berakhir dengan loop infinity :)
BJ
48

Bergantung pada browser mana yang Anda butuhkan untuk mendukung, ada opsi MutationObserver .

EDIT: Semua browser utama mendukung MutationObserver sekarang .

Sesuatu di sepanjang garis ini harus melakukan trik:

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

NB Saya belum menguji kode ini sendiri, tapi itu ide umum.

Anda dapat dengan mudah memperluas ini hanya untuk mencari bagian DOM yang berubah. Untuk itu, gunakan mutationsargumen, ini adalah array MutationRecordobjek.

sial
sumber
2
Saya suka yang ini. Terima kasih.
lambang
1
Pola ini sangat membantu dalam banyak kasus, terutama jika Anda menarik JS ke halaman dan tidak tahu apakah item lain dimuat.
Untuk Nama
1
Jawaban terbaik! Terima kasih!
Antony Hatchkins
1
Saya macet dengan peramban lama (ff38) dan ini menyelamatkan saya.
jung rhew
1
Ini luar biasa! Saya berharap saya tahu ini ada sebelumnya.
Rob
39

Ini hanya akan berfungsi dengan peramban modern tetapi saya merasa lebih mudah untuk hanya menggunakan thenjadi silakan uji dulu tetapi:

Kode

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

Atau menggunakan fungsi generator

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

Pemakaian

checkElement('body') //use whichever selector you want
.then((element) => {
     console.info(element);
     //Do whatever you want now the element is there
});
Jamie Hutber
sumber
Terjadi kesalahan. Saat menggunakan fungsi generator, pemilih kueri harus diperbarui di setiap loop:while (document.querySelector(selector) === null) {await rafAsync()}
haofly
32

Pendekatan yang lebih modern untuk menunggu elemen:

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

Perhatikan bahwa kode ini harus dibungkus dengan fungsi async .


sumber
4
ini cukup rapi!
Dexter Bengil
Ada apa rdisana
Daniel Möller
Baiklah, tapi dari mana asalnya? Apa fungsinya? Kamu mengirim pesan apa setTimeout?
Daniel Möller
@ DanielMöller Anda mungkin perlu melihat pada Janji untuk mendapatkan pemahaman yang lebih baik tentang kode ini. Pada dasarnya yang dilakukan oleh kode di sini adalah mengatur batas waktu 500 ms dan menunggu sampai selesai sebelum memulai iterasi baru dari loop sementara. Solusi pintar!
ClementParis016
8

Ini sedikit perbaikan dari jawaban Jamie Hutber

const checkElement = async selector => {

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

return document.querySelector(selector); };
wLc
sumber
8

Lebih baik untuk menyampaikan requestAnimationFramedaripada dalam setTimeout. ini adalah solusi saya dalam modul ES6 dan menggunakanPromises .

es6, modul dan janji:

// 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
  });
ncubica
sumber
Uncaught TypeError: Cannot read property 'then' of undefined
Jeff Puckett
Saya pikir saya melewatkan pengembalian ... sebelum Janji baru.
ncubica
1
Ini adalah solusi yang tepat, jauh lebih baik daripada semua pemeriksaan berbasis Timer berkala.
András Szepesházi
4
Sebenarnya, ini tidak berfungsi dalam bentuk saat ini. Jika $ someElement awalnya nol (artinya belum ada di DOM), maka Anda meneruskan nilai nol ini (bukan selektor CSS) ke fungsi onElementReady Anda dan elemen tidak akan pernah diselesaikan. Sebagai gantinya, sampaikan pemilih CSS sebagai teks dan cobalah untuk mendapatkan referensi ke elemen melalui .querySelector pada setiap pass.
András Szepesházi
1
Ini bekerja dengan baik untuk use case saya dan sepertinya solusi yang tepat untuk saya. Terima kasih
rdhaese
5

Berikut adalah solusi menggunakan yang bisa diamati.

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

Sekarang kamu bisa menulis

waitForElementToAppear(elementId).subscribe(el_ref => doSomethingWith(el_ref);
FrankL
sumber
4

Anda dapat memeriksa apakah dom sudah ada dengan mengatur batas waktu hingga dom diberikan di dalam dom.

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

Jika Anda menginginkan solusi umum menggunakan MutationObserver, Anda dapat menggunakan fungsi ini

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

Sumber: https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
Contoh cara menggunakannya

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

Catatan: MutationObserver memiliki dukungan browser yang hebat; https://caniuse.com/#feat=mutationobserver

Dan lagi! :)

rhhainaut
sumber
2

Variasi lain dari 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);

Kalau-kalau elemen tidak pernah ditampilkan, jadi kami tidak memeriksa tanpa batas.

heriberto perez
sumber