dart-lang / web

Lightweight browser API bindings built around JS static interop.
https://pub.dev/packages/web
BSD 3-Clause "New" or "Revised" License
135 stars 23 forks source link

getElementById returns null even though element was created previously #279

Closed phmv closed 3 months ago

phmv commented 3 months ago

We are using flutter-webrtc package in our project. Inside web implementation of RTCVideoRenderer we call initialize() method which creates an HTMLVideoElement() inside it:

final element = web.HTMLVideoElement()
        ..autoplay = true
        ..muted = true
        ..controls = false
        ..srcObject = _videoStream
        ..id = _elementIdForVideo
        ..setAttribute('playsinline', 'true');

Right after calling initialize() we use a setter setSrcObject which calls findHtmlView() method inside it. The method contains getting an Element by id final element = web.document.getElementById(_elementIdForVideo); but the problem is it returns null. How does it possible and why does it happen?

File that contains code described above: https://github.com/flutter-webrtc/flutter-webrtc/blob/main/lib/src/web/rtc_video_renderer_impl.dart Related issue opened in flutter-webrtc repository: https://github.com/flutter-webrtc/flutter-webrtc/issues/1649

srujzs commented 3 months ago

It looks like you have to add it to the document tree after you create it.

https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById#usage_notes

Elements not in the document are not searched by getElementById(). When creating an element and assigning it an ID, you have to insert the element into the document tree with Node.insertBefore() or a similar method before you can access it with getElementById()

You're also calling getElementById before you assign the id, since you're calling _videoStream before you assign id, so calling srcObject after you add the element to the document and give it an id should fix it.

phmv commented 3 months ago

@srujzs Thanks for your answer! I have something to discuss.

It looks like you have to add it to the document tree after you create it.

The constructor web.HTMLVideoElement() creates element in the dom tree and i guess inserts it by the registerViewFactory func, but looks like it doesn't happen synchronously. Because if i put some delay (10ms) between calling initialize() and findHtmlView() then getElementById always finds the element.

You're also calling getElementById before you assign the id, since you're calling _videoStream before you assign id

The line ..srcObject = _videoStream is setting a value to the element's srcObject attribute right after creating. It doesn't call getElementById since it's not the same thing as srcObject setter of RTCVideoRenderer`

srujzs commented 3 months ago

The constructor web.HTMLVideoElement() creates element in the dom tree and i guess inserts it by the registerViewFactory func, but looks like it doesn't happen synchronously. Because if i put some delay (10ms) between calling initialize() and findHtmlView() then getElementById always finds the element.

Yeah, that seems to be the case. It looks like the passed factory is called later in a call to PlatformViewManager.renderContent which then appends it to the document. I'm not sure what the workflow here is supposed to look like so that that's always called first before findHtmlView, so it might be worth asking the Flutter folks.

The line ..srcObject = _videoStream is setting a value to the element's srcObject attribute right after creating. It doesn't call getElementById since it's not the same thing as srcObject setter of RTCVideoRenderer`

Ah right, my mistake. That should be fine then.

phmv commented 3 months ago

So i am going to close the issue and leave solving it to flutter-webrtc contributors. Thank you for productive discussion, @srujzs