Problem/Motivation

Since the fix for #3538028 in 3.0.18, dBlazy.find(el, selector) silently falls back to querying the entire document whenever el has no descendant matching selector, instead of returning nothing. See context() in js/src/dblazy.js.

Drupal.behaviors.blazyMedia (js/src/components/blazy.media.js) relies on scoped, possibly-empty lookups like $el.find('iframe') to find an iframe already inside a not-yet-clicked .media--player element (there normally isn't one) and unconditionally calls $.remove() on the result. Because of the fallback above, that "not found" lookup instead returns the first iframe anywhere on the page, and blazyMedia deletes it — even if it has nothing to do with Blazy.

  • Confirmed with a page that has a Blazy oEmbed field (click-to-play, unclicked) plus an unrelated iframe elsewhere (e.g. a plain core Media oembed formatter field, appearing earlier in the DOM)
  • The unrelated iframe is silently removed from the DOM on page load, with no console error
  • Confirmed via instrumented Node.prototype.removeChild, stack trace shows removal originates from blazyMedia's attach → internal process()dBlazy.remove()
  • Confirmed root cause by patching dBlazy.find at runtime to not fall back to document on no-match — the unrelated iframe then survives

Note: the 3.0.19 changelog entry "Fixed for Colorbox local/remote video collapsed against modern CSS aspect ratio" is CSS-only (css/components/blazy.media.css) and does not touch this code path. It does not fix this bug, since the affected iframe is removed from the DOM entirely rather than collapsed by CSS.

Steps to reproduce

  • On one page, add a Blazy-formatted oEmbed/video field using the click-to-play media--player pattern, left in its default unclicked state
  • On the same page, earlier in DOM order, add any other iframe that loads on page load and is unrelated to Blazy (e.g. a core Media oembed formatter field, a raw embedded video/iframe, a share-widget iframe)
  • Load the page and let Drupal.attachBehaviors run
  • Observe the earlier, unrelated iframe disappears from the DOM

Proposed resolution

In js/src/components/blazy.media.js, verify a found iframe is actually a descendant of the relevant player element before removing it, rather than trusting find()'s fallback semantics, e.g.:

var iframe = $el.find(IFRAME);
if (iframe && el.contains(iframe)) {
  $.remove(iframe);
}

Apply the same guard to the other $.remove(iframe) calls in play()/playNow()/stop() in the same file that rely on scoped find() results.

Separately, context() in js/src/dblazy.js should not widen an element-scoped query to document merely because nothing matched — "no match" is a valid, expected result of a scoped query.

Remaining tasks

  • Confirm whether the context() fallback is still needed for the original #3538028 case once blazy.media.js stops relying on it, or whether it needs tightening rather than removal
  • Add JS test coverage for a page containing both a click-to-play Blazy player and an unrelated third-party iframe

User interface changes

None.

API changes

None.

Data model changes

None.

Issue fork blazy-3619552

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

freelock created an issue. See original summary.

freelock’s picture

Status: Active » Needs review

Fix in MR. Issue summary and patch created with assistance from Claude Sonnet.

gausarts’s picture

Status: Needs review » Postponed (maintainer needs more info)

Thank you.

I haven't been able to reproduce or trace the root cause.

Just based-on my own understanding:

  1. dBlazy.remove() is scoped to parentElement, hence el=.media--player, never document. Not a culprit as you suspected.
  2. dBlazy.find() does fallback to document, which likely should not, but no real issues so far, since it's usage in blazyMedia process is already scoped to el=.media--player, not document as you suspected. Not a culprit, either.
  3. process() will not be invoked if no elements are found. Thus no document fallback here:
    https://git.drupalcode.org/project/blazy/-/blob/3.0.19/js/src/plugin/bla...
  4. Sounds like dBlazy.context() is the culprit? But I am hesitant since it is still scoped to .media--player as the selector. Thus element in process() is always a .media--player, never fallback to document as you suspected.

    To prove it is the culprit, please remove this line:
    https://git.drupalcode.org/project/blazy/-/blob/3.0.x/js/src/components/...

    If that solves your issues, please update your patch with that alone.

    Leave the rest of $.remove() intact for now. If persists, then adjust them as you did.

A better solution might be to change iframe selector to iframe.media__element as needed.

If anyone has the same issue, please confirm. I may need some time to investigate this any better.