Fonksiyon var olana kadar bekletme


159

Başka bir tuval üzerine bir tuval eklemeye çalışıyorum - ilk tuval oluşturulana kadar bu işlevi nasıl başlatabilirim?

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

4
Tıklandığında tuvali oluşturduğunuzda, işlevi çalıştırın veya işlevi çalıştıran bir işleyici çalıştıran bir olayı tetikleyin. bir öğe kullanılabilir duruma geldiğinde gerçekleşen yerleşik bir tarayıcılar arası etkinlik yoktur.
Kevin B

Yanıtlar:


305

Tuvali oluşturan koda erişiminiz varsa - tuval oluşturulduktan hemen sonra işlevi çağırın.

Bu koda erişiminiz yoksa (ör. Google maps gibi bir üçüncü taraf kodu ise) yapabileceğiniz şey, bir aralıkta varlığını test etmektir:

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

Ancak unutmayın - çoğu zaman 3. taraf kodunun yüklenmesi tamamlandığında kodunuzu (geri arama veya olay tetikleme yoluyla) etkinleştirme seçeneği vardır. Fonksiyonunuzu koyabileceğiniz yer burası olabilir. Aralıklı çözüm gerçekten kötü bir çözümdür ve sadece başka hiçbir şey işe yaramazsa kullanılmalıdır.


angularjs typeahead kullanım için mükemmel bir çözüm. Beni doğru yönde yönlendirdiğiniz için teşekkürler!
JuanTrev

1
Orada başka bir şey koymadan önce Ajax tarafından yaratılan bir şey beklemek için mükemmel bir çözüm. Çok teşekkürler.
Countzero

@ iftah Seçici değişkense bunu nasıl çalıştırabilirim? Ayrıca, bir ID veya Sınıf seçici ise de değişir. Bazen bir sınıfla seçtiğimde döndürülen birden çok öğe vardır ve hangisini bulmak için seçiciye bir dizin iletmenin bir yolunu bulmam gerekirdi. Bunu nasıl yaparım? Teşekkürler
Kragalon

@Kraglon bu tamamen farklı bir soru ve bu cevabın yorumları için uygun değil. Yeni bir soru sormanızı, ne denediğinizi, sorunun ne olduğunu vb.
Açıklamanızı öneririm

8
Verilen çözümü kullanırken belirtilmesi gereken bir şey daha var, bir for döngüsü içinde bu kod parçasına sahip olmalısınız ve bir maksimum yeniden deneme sayacı ayarlamalısınız, bir şeyler ters giderse sonsuzluk döngüsü ile sonuçlanmaz :)
BJ

48

Hangi tarayıcıyı desteklemeniz gerektiğine bağlı olarak MutationObserver seçeneği vardır .

EDIT: Tüm büyük tarayıcılar şimdi MutationObserver'ı destekliyor .

Bunun çizgileri boyunca bir şey hile yapmalıdır:

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

Not: Bu kodu kendim test etmedim, ama genel fikir bu.

Bunu, yalnızca DOM'nin değişen bölümünde arama yapmak için kolayca genişletebilirsiniz. Bunun için mutationsargümanı kullanın , bu bir MutationRecordnesne dizisidir .


2
Bunu sevdim. Teşekkür ederim.
INSIGN

1
Bu desen, özellikle JS'yi bir sayfaya çekiyorsanız ve diğer öğelerin yüklü olup olmadığını bilmiyorsanız, birçok durumda gerçekten yararlıdır.
İsim

1
En iyi cevap! Teşekkürler!
Antony Hatchkins

1
Eski bir tarayıcıya takılı kaldım (ff38) ve bu beni kurtardı.
jung rhew

1
Bu harika! Keşke bunun daha önce var olduğunu bilseydim.
Rob

39

Bu sadece modern tarayıcılarla çalışacaktır, ancak sadece bir thentesti kullanmayı daha kolay buluyorum, lütfen önce test edin ama:

kod

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

Veya jeneratör fonksiyonlarını kullanarak

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

kullanım

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

Bir hata var. Jeneratör işlevlerini kullanırken, querySelector her döngüde güncellenmelidir:while (document.querySelector(selector) === null) {await rafAsync()}
haofly

33

Elemanları beklemeye daha modern bir yaklaşım:

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

Bu kodun zaman uyumsuz bir işleve sarılması gerektiğini unutmayın .


4
bu çok temiz!
Dexter Bengil

rOrada ne var?
Daniel Möller

Peki, tamam, ama nereden geliyor? Bu ne işe yarıyor? Ne gönderiyorsun setTimeout?
Daniel Möller

@ DanielMöller , bu kodu daha iyi anlamak için Promises'a bir göz atmanız gerekebilir . Temel olarak kodun yaptığı şey 500 ms'lik bir zaman aşımı ayarlamak ve while döngüsünün yeni bir yinelemesini başlatmadan önce tamamlanmasını bekleyin. Akıllı çözüm!
ClementParis016

8

İşte Jamie Hutber'in cevabı üzerinde ufak bir gelişme

const checkElement = async selector => {

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

return document.querySelector(selector); };

8

Geçiş için daha iyi mi requestAnimationFramebir daha setTimeout. Bu benim es6 modülleri ve kullanma benim çözümdür Promises.

es6, modüller ve vaatler:

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

Uncaught TypeError: Cannot read property 'then' of undefined
Jeff Puckett

Sanýrým yeni Sözden önce bir geri dönüşü kaçırdým.
ncubica

1
Bu, periyodik Zamanlayıcı tabanlı kontrollerden çok daha iyi olan doğru çözümdür.
András Szepesházi

4
Aslında, bu mevcut haliyle çalışmaz. $ SomeElement başlangıçta null ise (yani henüz DOM'da yoksa), bu null değeri (CSS seçici yerine) onElementReady işlevinize iletirsiniz ve öğe hiçbir zaman çözülmez. Bunun yerine, CSS seçicisini metin olarak iletin ve her geçişte .querySelector aracılığıyla öğeye başvuru almaya çalışın.
András Szepesházi

1
Bu benim kullanım durumum için harika çalıştı ve bana uygun bir çözüm gibi görünüyor. Thanks
rdhaese

5

İşte gözlemlenebilirler kullanan bir çözüm.

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

Şimdi yazabilirsin

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

4

Dom'da zaten görüntülenene kadar bir zaman aşımı ayarlayarak domun zaten var olup olmadığını kontrol edebilirsiniz.

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

4

MutationObserver kullanarak genel bir çözüm istiyorsanız, bu işlevi kullanabilirsiniz

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

Kaynak: https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
Nasıl kullanılacağı örneği

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

Not: MutationObserver'ın mükemmel bir tarayıcı desteği vardır; https://caniuse.com/#feat=mutationobserver

Et voilà! :)


2

İftah'ın bir başka varyasyonu

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

Eleman asla gösterilmiyorsa, sonsuz kontrol yapmayız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.