2013-04-22 3 views
61

Я пытаюсь добавить холст поверх другого холста - как я могу заставить эту функцию ждать, пока не будет создан первый холст?Сделать функцию до тех пор, пока не существует элемент

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

Когда вы создаете холст при щелчке, запускаете функцию или запускаете событие, которое запускает обработчик, который запускает эту функцию. нет встроенного кросс-браузерного события, которое происходит, когда элемент становится доступным. –

+0

Возможный дубликат [Как подождать, пока элемент существует?] (Http://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists) – user2284570

ответ

132

Если у вас есть доступ к коду, который создает холст, просто вызовите функцию прямо после создания холста.

Если у вас нет доступа к этому коду, то, что вы могли бы сделать, это тест на наличие в интервале (например, если это 3-ий код партии, такие как Google Maps.):

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

Но обратите внимание - во многих случаях у стороннего кода есть возможность активировать ваш код (путем обратного вызова или запуска события), когда он закончит загрузку. Это может быть место, где вы можете поставить свою функцию. Решение интервалов действительно плохое решение и должно использоваться только в том случае, если ничего не работает.

+0

Я использовал setTimeout для запуска запуска моего сценария после щелчка спасибо – Steven

+0

идеальное решение для использования в angularjs typeahead. Спасибо, что направили меня в правильном направлении! – JuanTrev

+1

Отличное решение для ожидания создания чего-либо, созданного Ajax, перед тем как положить туда что-то другое. Большое спасибо. – Countzero

2

Это очень просто - не вызывать эту функцию, пока вы уже не создали полотно (то есть сделать это в пределах этого click обработчика).

1

Вы можете проверить, существует ли 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); 
14

В зависимости от того, какой браузер вы должны поддерживать, есть вариант 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 объектов.

2

Лучше реле в 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 
    } 
+0

'Uncaught TypeError: Невозможно прочитать свойство 'then' of undefined' –

+0

Кажется, я пропустил возвращение ... перед новым обещанием. – ncubica

+0

Это правильное решение, намного лучше, чем все периодические проверки на основе таймера. –

2

Это будет работать только с современными браузерами, но я считаю, что проще всего использовать 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) { 
    while (document.querySelector(selector) === null) { 
     await rafAsync() 
    } 
    return true; 
} 

Использование

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

Более современный подход к ожиданию элементов:

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

Обратите внимание, что этот код необходимо будет обернуть в async function.

Смежные вопросы