<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ryo</title>
    <description>The latest articles on DEV Community by Ryo (@ryo-karasunouta).</description>
    <link>https://dev.to/ryo-karasunouta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4014798%2F69c40c00-431b-4bb0-8258-4642d33ad8f3.png</url>
      <title>DEV Community: Ryo</title>
      <link>https://dev.to/ryo-karasunouta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryo-karasunouta"/>
    <language>en</language>
    <item>
      <title>How to Safely Integrate the YouTube API in Lazy-Loading Environments Using MutationObserver</title>
      <dc:creator>Ryo</dc:creator>
      <pubDate>Mon, 20 Jul 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/ryo-karasunouta/how-to-safely-integrate-the-youtube-api-in-lazy-loading-environments-using-mutationobserver-1fhn</link>
      <guid>https://dev.to/ryo-karasunouta/how-to-safely-integrate-the-youtube-api-in-lazy-loading-environments-using-mutationobserver-1fhn</guid>
      <description>&lt;p&gt;In the recently published article “&lt;a href="https://karasunouta.com/en/web-tech/how-to-load-the-youtube-iframe-player-api-without-breaking-other-plugins/" rel="noopener noreferrer"&gt;How to Load the YouTube IFrame Player API Without Breaking Other Plugins&lt;/a&gt;,” we discussed how to prevent conflicts in environments where multiple plugins attempt to initialize the YouTube API simultaneously by incorporating a spirit of mutual concession (polling, sharing existing players, and random jitter) into the code.&lt;/p&gt;

&lt;p&gt;The WordPress plugin “&lt;a href="https://karasunouta.com/en/wp-plugins/ku-sticky-video-for-youtube/" rel="noopener noreferrer"&gt;KU Sticky Video for YouTube&lt;/a&gt;,” which incorporates this design, had been operating smoothly on this site without any conflict issues. However, a user reported that &lt;strong&gt;the sticky video feature stopped working when used in combination with lazy-loading (LazyLoad) plugins&lt;/strong&gt;. Upon verification, this was indeed the case!&lt;/p&gt;

&lt;p&gt;The polling method discussed in the previous article assumes that the video player (&lt;code&gt;iframe&lt;/code&gt;) itself already exists in a static state within the DOM at the time of page load. However, in a lazy-loading environment where video players are loaded dynamically in response to user actions such as scrolling or clicking, the target &lt;code&gt;iframe&lt;/code&gt; has not yet been generated at page load. Admittedly, this was a blind spot.&lt;/p&gt;

&lt;p&gt;This article explains a dynamic and asynchronous binding design for the YouTube API using &lt;code&gt;MutationObserver&lt;/code&gt; to overcome the new challenge of coexisting with LazyLoad plugins.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Does It Not Work? The YouTube API Under LazyLoad
&lt;/h2&gt;

&lt;p&gt;To improve initial page load performance, LazyLoad plugins delay the loading of images and &lt;code&gt;iframe&lt;/code&gt;s using approaches such as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Attribute rewriting:&lt;/strong&gt; The plugin assigns a placeholder or empty value to the &lt;code&gt;src&lt;/code&gt; attribute, moving the actual URL to a separate attribute such as &lt;code&gt;data-src&lt;/code&gt;. Once the element approaches the viewport during scrolling, JavaScript writes the actual URL back to the &lt;code&gt;src&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dynamic DOM insertion:&lt;/strong&gt; The &lt;code&gt;iframe&lt;/code&gt; element itself is not generated in the DOM until it enters the viewport. Instead, a placeholder image of the video player is displayed. When the play button on the placeholder image is clicked, the &lt;code&gt;iframe&lt;/code&gt; is dynamically generated and displayed in its place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In contrast, many YouTube-related plugins, including KU Sticky Video for YouTube, execute &lt;code&gt;document.querySelectorAll('iframe')&lt;/code&gt; at timings such as &lt;code&gt;DOMContentLoaded&lt;/code&gt; to perform initialization and register event listeners on the elements existing at that moment. However, when LazyLoad is enabled, the iframe elements for embedded YouTube videos are either not yet generated at this timing, or are intentionally generated in an incomplete state.&lt;/p&gt;

&lt;p&gt;Plugins dependent on the YouTube API attempt to search for target iframes under these conditions. However, no matching iframes are found. As a result, the initialization process is skipped, and the plugin’s functionality—in the case of KU Sticky Video for YouTube, the sticky video feature that follows the scroll—never triggers.&lt;/p&gt;

&lt;p&gt;The polling-based countermeasure implemented in the previous article was also largely ineffective against LazyLoad plugins. Since polling begins immediately after page load, the delay in execution is at most a few seconds. Meanwhile, it is impossible to predict when a reader will scroll down the article. While one could theoretically poll indefinitely, &lt;strong&gt;polling requires periodic timer events&lt;/strong&gt;, making indefinite waiting impractical.&lt;/p&gt;

&lt;p&gt;After all, a reader might step away to the restroom immediately after opening the page, bump into a colleague in the hallway on the way back, chat for a while, and only begin scrolling the article after finally returning to their seat. If &lt;code&gt;setInterval&lt;/code&gt; continues to run for minutes, or potentially over an hour, while waiting for them, the browser cannot enter an idle state. Especially on mobile devices, this would result in &lt;strong&gt;the birth of a “mysteriously battery-draining page.”&lt;/strong&gt; We would certainly like to avoid that.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Dynamic Binding Using MutationObserver
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Detecting Videos and Preventing Double Initialization with MutationObserver
&lt;/h3&gt;

&lt;p&gt;To accommodate various lazy-loading approaches, we configure the &lt;code&gt;MutationObserver&lt;/code&gt; to monitor two types of mutation events simultaneously: the dynamic addition of DOM nodes and changes to the &lt;code&gt;src&lt;/code&gt; attribute of existing elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initMutationObserver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detectedIframes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="nx"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Pattern A: Monitor the dynamic addition of DOM nodes&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;childList&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addedNodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodeType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ELEMENT_NODE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IFRAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nx"&gt;detectedIframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="c1"&gt;// Extract nested iframes as well&lt;/span&gt;
                            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nested&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                            &lt;span class="nx"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;detectedIframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; 

            &lt;span class="c1"&gt;// Pattern B: Monitor changes to the src attribute of existing elements&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attributes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributeName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodeType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ELEMENT_NODE&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IFRAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;detectedIframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Filter and process only YouTube iframes from the detected ones&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;youtubeIframes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;detectedIframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isYouTubeIframe&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;youtubeIframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;handleNewIframes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;youtubeIframes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;childList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;subtree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;attributeFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// Restrict monitoring to the src attribute to minimize performance impact&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the implementation to instantly capture the appearance of a video, regardless of the mechanism the LazyLoad plugin uses to delay it. Specifying &lt;code&gt;attributeFilter: ['src']&lt;/code&gt; limits the attribute monitoring to &lt;code&gt;src&lt;/code&gt;, minimizing unnecessary overhead.&lt;/p&gt;

&lt;p&gt;Furthermore, since a &lt;code&gt;MutationObserver&lt;/code&gt; triggers frequently, detection events can occur multiple times for the same &lt;code&gt;iframe&lt;/code&gt;. If double initialization occurs, it would hinder not only coexistence with other plugins but also the plugin’s own processing. To reliably prevent this double processing, we assign a custom &lt;code&gt;data-&lt;/code&gt; attribute to the &lt;code&gt;iframe&lt;/code&gt; under process to manage its state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isYouTubeIframe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IFRAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Exclude elements already processed by this plugin&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-ku-sticky-processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;youtube.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
           &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;youtube-nocookie.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
           &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;youtu.be&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleNewIframes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Mark as processed&lt;/span&gt;
        &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-ku-sticky-processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Execute initialization and binding for this iframe&lt;/span&gt;
        &lt;span class="nf"&gt;initPlayingMode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elements with the &lt;code&gt;data-ku-sticky-processed&lt;/code&gt; attribute are immediately excluded from future &lt;code&gt;MutationObserver&lt;/code&gt; detections, reliably avoiding unnecessary loops and conflict errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamically Triggered Conflict Avoidance Process
&lt;/h3&gt;

&lt;p&gt;This is the core of the “dynamic binding” design. While the polling and random jitter introduced in the &lt;a href="https://karasunouta.com/en/web-tech/how-to-load-the-youtube-iframe-player-api-without-breaking-other-plugins/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt; were triggered based on a static timing like page load, the current design using &lt;code&gt;MutationObserver&lt;/code&gt; shifts to a dynamic model: &lt;strong&gt;every time a video appears on the page, an asynchronous process starting from conflict avoidance (mutual concession) to safe binding is initiated specifically for that video.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initPlayingMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Insert the YouTube API script if it has not been loaded yet&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script[src*="youtube.com/iframe_api"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/iframe_api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Start polling for this video, waiting for other plugins to complete initialization&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;apiReadyAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;setupCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkInterval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;attempts&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isApiReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Player&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isApiReady&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;apiReadyAttempts&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Check if other plugins have already created a player instance for this iframe&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isApiReady&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Termination condition: shared target found or wait time limit exceeded&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shouldStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;apiReadyAttempts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shouldStop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkInterval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;setupCompleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;setupCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="c1"&gt;// 3. Apply random jitter (0 to 100ms delay)&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jitterDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;setupPlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;jitterDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this asynchronous design, the following scenario executes autonomously. The actors involved are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;KU Sticky Video for YouTube plugin&lt;/strong&gt;: The subject of this article. Referred to as “this plugin.”&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;LazyLoad plugin&lt;/strong&gt;: A plugin that reduces page weight by lazy-loading YouTube videos during scrolling or other actions. Examples include &lt;a href="https://wordpress.org/plugins/lazy-load-for-videos/" rel="noopener noreferrer"&gt;Lazy Load for Videos&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Other plugins dependent on the YouTube API&lt;/strong&gt;: Third-party plugins that use the YouTube API to apply various effects to YouTube videos on the page. Referred to as “other plugins.”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The primary goal of this article is to make this plugin coexist with the lazy-loading plugin (No. 2). While coexistence with other plugins (No. 3) was achieved in the previous article, our objective here is to &lt;strong&gt;ensure that coexistence with other plugins remains intact even after achieving compatibility with LazyLoad plugins.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The actual execution flow is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; When a visitor scrolls down the page or performs a similar action, the &lt;strong&gt;LazyLoad plugin&lt;/strong&gt; loads the YouTube video (iframe).&lt;/li&gt;
&lt;li&gt; The &lt;code&gt;MutationObserver&lt;/code&gt; of &lt;strong&gt;this plugin&lt;/strong&gt; detects its appearance, checks for the presence of the API, and starts polling.&lt;/li&gt;
&lt;li&gt; Simultaneously, the &lt;strong&gt;other plugin&lt;/strong&gt; initiates its own initialization (player creation) process for the same iframe.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;This plugin&lt;/strong&gt; polls the initialization state of the other plugin every 100ms. If it detects that the &lt;strong&gt;other plugin&lt;/strong&gt; has created a player instance using &lt;code&gt;YT.get()&lt;/code&gt;, it safely shares events with that instance.&lt;/li&gt;
&lt;li&gt; If the &lt;strong&gt;other plugin&lt;/strong&gt; does not create a player instance, &lt;strong&gt;this plugin&lt;/strong&gt; creates its own player instance after a random jitter delay.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By keeping the process for each video completely independent, the necessary processing is executed at the exact moment the video appears, regardless of how long the reader waits before scrolling. This implementation maximizes coexistence not only with LazyLoad plugins but also with other YouTube API-dependent plugins (refer to the previous article for details on the role of random jitter).&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Coexistence Adapting to an Expanded Time Dimension
&lt;/h2&gt;

&lt;p&gt;In plugin development, conflicts with lazy loading (LazyLoad) are a common challenge. Previously, initialization order was adjusted within a tight window of a few seconds immediately after page load. LazyLoad, however, introduces an unpredictable and extended time dimension dictated by the reader’s scroll speed. The KU Sticky Video for YouTube plugin previously failed to account for this variable time frame, which was a significant oversight.&lt;/p&gt;

&lt;p&gt;One could avoid conflicts by declaring incompatibility with LazyLoad plugins and requesting users to disable them, but this compromises the user’s desire to optimize site loading speed. Fortunately, the combination of &lt;code&gt;MutationObserver&lt;/code&gt; and dynamic asynchronous polling implemented here allows the KU Sticky Video for YouTube plugin to operate stably while preserving the performance benefits of lazy-loading plugins.&lt;/p&gt;

&lt;p&gt;To achieve coexistence through &lt;strong&gt;mutual concession&lt;/strong&gt;, it is crucial to detect changes in the environment and trigger actions accordingly. I am reminded that this approach is an essential design pattern for ensuring that various third-party scripts coexist smoothly and perform as intended in the complex WordPress ecosystem.&lt;/p&gt;

&lt;p&gt;The dynamic binding design introduced in this article has already been deployed in updates for the free version (v1.9.0 and later) and the Pro version (v1.4.0 and later) of “KU Sticky Video for YouTube.” The sticky video feature should now work reliably even when lazy loading is active. If you have any feedback, please feel free to let us know via the &lt;a href="https://karasunouta.com/en/contact/" rel="noopener noreferrer"&gt;contact form&lt;/a&gt;!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://karasunouta.com/en/wp-plugins/ku-sticky-video-for-youtube/" rel="noopener noreferrer"&gt;WordPress plugin that makes the video player follow the scroll (Demo &amp;amp; Introduction)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://wordpress.org/plugins/ku-sticky-video-for-youtube/" rel="noopener noreferrer"&gt;KU Sticky Video for YouTube – WordPress plugin | WordPress.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://karasunouta.com/en/store/ku-sticky-video-for-youtube-pro/" rel="noopener noreferrer"&gt;KU Sticky Video for YouTube Pro (Premium Store)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>wordpress</category>
      <category>javascript</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Load the YouTube IFrame Player API Without Breaking Other Plugins</title>
      <dc:creator>Ryo</dc:creator>
      <pubDate>Fri, 10 Jul 2026 13:39:04 +0000</pubDate>
      <link>https://dev.to/ryo-karasunouta/how-to-load-the-youtube-iframe-player-api-without-breaking-other-plugins-204i</link>
      <guid>https://dev.to/ryo-karasunouta/how-to-load-the-youtube-iframe-player-api-without-breaking-other-plugins-204i</guid>
      <description>&lt;p&gt;When developing plugins for WordPress, one issue that almost everyone faces at least once is &lt;strong&gt;plugin conflicts&lt;/strong&gt;. The other day, I was developing a plugin (&lt;a href="https://karasunouta.com/en/wp-plugins/ku-sticky-video-for-youtube/" rel="noopener noreferrer"&gt;KU Sticky Video for YouTube&lt;/a&gt;) that makes YouTube videos in posts follow the scroll to stay in the corner of the screen. It worked perfectly in a clean local testing environment, and I was proud, thinking, “All right, it’s complete!”&lt;/p&gt;

&lt;p&gt;However, as soon as I moved the testing ground to a multi-plugin environment where another of my own YouTube-related plugins (unreleased) and other video-related plugins were active, things changed completely. The sticky feature, which had been working smoothly until just moments before, fell completely silent without moving an inch, and when I opened the browser console, there was that annoying red error text. &lt;strong&gt;Oh no.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But this is simply a common occurrence in plugin development. In reality, the solution varies depending on the cause of the conflict. In this article, I will explain the mechanism behind the “struggle for leadership” caused by the YouTube Iframe API, which many YouTube-related plugins depend on, in a multi-plugin environment, and introduce a JavaScript design to avoid this problem, share the ride with other scripts, and achieve coexistence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the Conflict? The YouTube API Struggle for Leadership
&lt;/h2&gt;

&lt;p&gt;To highly control YouTube videos from JavaScript, you need to load the official &lt;a href="https://developers.google.com/youtube/iframe_api_reference" rel="noopener noreferrer"&gt;YouTube Iframe Player API&lt;/a&gt;. In typical tutorial articles, implementation code like the following is usually introduced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Load the API script&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/iframe_api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;firstScriptTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define the global function called when loading is complete&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onYouTubeIframeAPIReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;player-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onStateChange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;onPlayerStateChange&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this code seems perfectly fine at first glance, it causes issues in &lt;strong&gt;environments where multiple plugins and themes run asynchronously&lt;/strong&gt;, such as in WordPress. Put simply, what happens if another plugin defines &lt;code&gt;window.onYouTubeIframeAPIReady&lt;/code&gt; in exactly the same way? Due to the nature of JavaScript, the initialization function defined first will be completely overwritten and lost by the plugin execution that runs later.&lt;/p&gt;

&lt;p&gt;As a result, the plugin that loaded first cannot detect when the API has finished loading, and it will never start. This is exactly why the initial version of KU Sticky Video for YouTube stopped working.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approaches to a Solution: How to Avoid “Fights” Between Plugins
&lt;/h2&gt;

&lt;p&gt;To prevent this conflict and ensure safe operation in any environment, we decided to take the following countermeasures in the KU Sticky Video for YouTube plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. “Script Presence Check” to Fill the Gap in Asynchronous Loading
&lt;/h3&gt;

&lt;p&gt;When the loading of the YouTube API script is complete, a &lt;code&gt;window.YT&lt;/code&gt; object is automatically created in the browser’s global scope (&lt;code&gt;window&lt;/code&gt;). This &lt;code&gt;window.YT&lt;/code&gt; object contains all the actual functions of the API for creating and controlling the YouTube player.&lt;/p&gt;

&lt;p&gt;The tricky part is that it is not as simple as, “I see. Then I just need to check if &lt;code&gt;window.YT&lt;/code&gt; exists.” If you do only that, what happens if the script runs &lt;strong&gt;“during the network load (when &lt;code&gt;window.YT&lt;/code&gt;&amp;nbsp;is undefined) right after another plugin inserted the tag”&lt;/strong&gt;? The script tag would be inserted twice. To prevent this, in addition to &lt;code&gt;window.YT&lt;/code&gt;, we also check whether a script tag for the YouTube API already exists in the DOM tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Search by partial match, considering URL notation variants (iframe_api / player_api)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alreadyLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script[src*="youtube.com/iframe_api"], script[src*="youtube.com/player_api"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;alreadyLoading&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Insert the script tag for the first time here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs.w.org%2Fimages%2Fcore%2Femoji%2F17.0.2%2Fsvg%2F1f4a1.svg" alt="💡" width="36" height="36"&gt; &lt;strong&gt;Why continue to check window.YT as well?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;At first glance, checking only whether the script tag exists in the DOM (&lt;code&gt;alreadyLoading&lt;/code&gt;) seems sufficient. After all, &lt;code&gt;window.YT&lt;/code&gt; is supposed to be created after the script is loaded. However, there may be cases where a script tag for the YouTube API does not exist in the DOM—for instance, if another plugin bundles the API directly into its JS via build tools like Vite and loads it.&lt;/p&gt;

&lt;p&gt;Therefore, it is necessary to perform a double check using both &lt;code&gt;window.YT&lt;/code&gt; to 100% guarantee that the load is already complete, and &lt;code&gt;alreadyLoading&lt;/code&gt; to detect via the script tag that “the asynchronous load is in progress right now.” By using both together, the risk of conflicts can be minimized.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. “Event Sharing (Binding)” via window.YT.get()
&lt;/h3&gt;

&lt;p&gt;If another plugin has already created a &lt;code&gt;YT.Player&lt;/code&gt; instance for a YouTube iframe on the page, executing &lt;strong&gt;&lt;code&gt;new YT.Player&lt;/code&gt; again on top of it will cause an error&lt;/strong&gt;. Therefore, we use the YouTube API global method &lt;code&gt;window.YT.get()&lt;/code&gt; to retrieve the already-created player instance, if it exists, and “share the ride” by binding only our event listeners to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;existingPlayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iframeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Get by the official spec if an ID exists. Otherwise, fall back to passing the DOM element.&lt;/span&gt;
    &lt;span class="nx"&gt;existingPlayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;iframeElement&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;existingPlayer&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Share the event with the existing instance if it exists&lt;/span&gt;
    &lt;span class="c1"&gt;// * Unlike standard DOM, the YouTube API's addEventListener requires the "on" prefix (onStateChange)&lt;/span&gt;
    &lt;span class="nx"&gt;existingPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onStateChange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;handlePlayerStateChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Otherwise, create a new instance&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although the official argument for &lt;code&gt;YT.get()&lt;/code&gt; is the “iframe ID string,” we set up a two-step binding process using the unofficial yet highly compatible method of passing the DOM element as a fallback in case there is no ID attribute.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. “Polling Control” to Prevent Timing Conflicts
&lt;/h3&gt;

&lt;p&gt;The core of these countermeasures is &lt;strong&gt;timing control&lt;/strong&gt; to determine when the API load is complete and when other plugins have finished initialization. For this purpose, we decided to adopt a technique called polling.&lt;/p&gt;

&lt;p&gt;However, the YouTube API comes with a global callback function &lt;code&gt;window.onYouTubeIframeAPIReady&lt;/code&gt; that is automatically executed by the API when the script loading is complete. Why was such a roundabout method like polling necessary instead of monitoring that standard event handler?&lt;/p&gt;

&lt;h4&gt;
  
  
  3-1. The Asynchronous Risk Inherent in Wrappers for the Standard “onYouTubeIframeAPIReady”
&lt;/h4&gt;

&lt;p&gt;When considering conflicts with other plugins, a common approach introduced as a standard trick on many development blogs is to “temporarily save the existing callback function into a variable, wrap it, and execute both the other plugin’s initialization and your own in succession” as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Save the existing callback if there is one&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previousOnReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onYouTubeIframeAPIReady&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Overwrite the global callback and execute the other processes in succession&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onYouTubeIframeAPIReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;previousOnReady&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;previousOnReady&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;myPluginInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Initialize our own plugin&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this seems like a perfect solution for coexistence at first glance, this implementation actually carries a fatal risk: &lt;strong&gt;it cannot handle cases where other plugins initialize the player with asynchronous processing in between&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, suppose the other plugin we want to coexist with is designed to initialize by running an asynchronous process, such as “detecting API Ready, fetching configuration data via Ajax, and then creating the player.” In this case, our plugin’s (Sticky’s) initialization process has no way of knowing that the other plugin is waiting for its background asynchronous process to finish. Therefore, at the exact moment of API Ready, it determines that “no conflicting player instance exists yet” and goes ahead to create a new player (&lt;code&gt;new YT.Player&lt;/code&gt;) ahead of the other.&lt;/p&gt;

&lt;p&gt;By the time the other plugin finishes its asynchronous processing and tries to create its player, our plugin’s initialization is already complete. This causes a &lt;strong&gt;double-initialization error&lt;/strong&gt; for the same iframe element, causing the other plugin to fail. Therefore, if you want to write a robust implementation that accounts for conflicts, you cannot use this &lt;code&gt;onYouTubeIframeAPIReady&lt;/code&gt; callback directly as your own initialization trigger.&lt;/p&gt;

&lt;p&gt;Our plugin must not interfere at all, such as by overwriting or assigning to the global callback; it should simply load the API, monitor in the background to see “when the other plugin’s initialization is complete,” and then safely share the ride (bind) to it. This is precisely why we need time control using “polling,” which will be explained next.&lt;/p&gt;

&lt;h4&gt;
  
  
  3-2. The Solution: Controlling Initialization Timing with Polling
&lt;/h4&gt;

&lt;p&gt;To cleanly link the three approaches explained so far—wrapping, double-loading prevention, and event sharing—&lt;strong&gt;controlling the timing of the initialization process&lt;/strong&gt; is extremely important. This is where a design pattern called &lt;strong&gt;“polling”&lt;/strong&gt; comes in handy, which periodically checks the API load status and player initialization status from the program.&lt;/p&gt;

&lt;p&gt;In JavaScript, polling is mainly implemented using &lt;code&gt;setInterval&lt;/code&gt;. The actual initialization code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initPlayingMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Load the API after checking if another script has already been inserted&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script[src*="youtube.com/iframe_api"], script[src*="youtube.com/player_api"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/iframe_api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)[&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;firstScriptTag&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Poll and monitor the API load and the completion of other plugins' initialization&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;apiReadyAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;setupCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkInterval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;attempts&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isApiReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Player&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;isApiReady&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;apiReadyAttempts&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Check if other plugins have finished creating instances for all iframes&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;isApiReady&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;iframes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Determine the termination conditions&lt;/span&gt;
        &lt;span class="c1"&gt;// - Binding by other plugins is complete (allFound), or&lt;/span&gt;
        &lt;span class="c1"&gt;// - The grace period (300ms) to wait for other plugins' initialization after the API loads has passed (apiReadyAttempts &amp;gt;= 3), or&lt;/span&gt;
        &lt;span class="c1"&gt;// - The maximum timeout (3 seconds) has been reached (attempts &amp;gt;= 30)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shouldStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allFound&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;apiReadyAttempts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;shouldStop&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;checkInterval&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;setupCompleted&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;setupCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="c1"&gt;// Perform either "ride-sharing" or "new creation" for each iframe&lt;/span&gt;
                &lt;span class="nf"&gt;setupPlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;iframes&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the API is ready, instead of initializing the player immediately on our own, this code sets up a maximum wait time of 300ms (checking the status up to 3 times every 100ms = polling). If another plugin is attempting to asynchronously create a player at the moment of API Ready, the purpose is to wait a short while for its completion before sharing the ride, thereby preventing double-creation errors as much as possible.&lt;/p&gt;

&lt;p&gt;However, if you wait too long and our plugin’s initialization is delayed unnecessarily, there is a risk that the event listeners will not be registered yet by the time the video starts playing. As a result of trial and error balancing this trade-off, this 300ms wait time emerged as the empirical magic number (the best practical threshold) in this development process.&lt;/p&gt;

&lt;p&gt;This value might vary depending on the purpose of your plugin.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ultimate Question: When Seemingly Perfect Codes Meet
&lt;/h2&gt;

&lt;p&gt;With the implementation so far, we seem to have completed a mostly perfect conflict avoidance code that “does not interfere with the loading of other plugins,” “shares the ride on players created by other plugins,” and “waits 300ms to prevent double creation.” Some readers might also have agreed, thinking, “I see, this implementation looks like it should work fine.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is a question. If Plugin A and Plugin B—two separate plugins, both incorporating the “perfect implementation” explained in this article—are loaded on the same page at the same time, can they coexist?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer, unfortunately, is &lt;strong&gt;“yes, there are cases, albeit rare, where they will cause conflict errors and go silent (break down).”&lt;/strong&gt; The cause lies in the “asynchronous registration lag” of the YouTube API. In fact, there is a tiny lag of a few milliseconds between when you execute &lt;code&gt;new YT.Player()&lt;/code&gt; and when that player instance is registered inside the API and becomes retrievable via &lt;code&gt;window.YT.get()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If Plugins A and B, which share the same “300ms wait” logic, start their timers at the exact same API Ready timing, both will execute &lt;code&gt;YT.get()&lt;/code&gt; almost simultaneously after 300ms. At this moment, since neither has created a player yet, the return value for both will be &lt;code&gt;null&lt;/code&gt;. Consequently, both assume that no existing player exists, and both execute &lt;code&gt;new YT.Player()&lt;/code&gt; almost at the same time. As a result, a double-creation error occurs on the same iframe, causing both to fail.&lt;/p&gt;

&lt;p&gt;You might think, “Then why not detect double creation inside the &lt;code&gt;onReady&lt;/code&gt; callback and clean it up with &lt;code&gt;destroy()&lt;/code&gt;?” However, the YouTube API’s &lt;code&gt;destroy()&lt;/code&gt; forcibly rewrites the DOM elements and clears event handlers, carrying the fatal side effect of taking down and destroying the player that was already running normally. In other words, &lt;strong&gt;once a double creation has occurred, it cannot be safely removed afterward&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing “Random Jitter” as the Solution
&lt;/h3&gt;

&lt;p&gt;Since the YouTube API does not provide events for perfect synchronization across multiple plugins, there is no perfect code that can reduce the probability of this timing collision to 0%. However, the solution to suppress this collision probability to practically zero (an ignorable level) is the introduction of &lt;strong&gt;“random jitter”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What we do is very simple. After the 300ms grace period expires, instead of creating the new player immediately, we insert a &lt;strong&gt;“random delay of 0 to 100ms” right before execution&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Executed after the 300ms monitoring termination condition is met&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;shouldStop&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;checkInterval&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;setupCompleted&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;setupCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Disperse execution timing by inserting a random jitter (delay) of 0 to 100ms&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jitterDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Check window.YT.get() once more after the random delay expires&lt;/span&gt;
            &lt;span class="nf"&gt;setupPlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;iframes&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;jitterDelay&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding a random delay, there is a high probability of a discrepancy in the execution timing of Plugin A and Plugin B, which started moving at the same time.&lt;/p&gt;

&lt;p&gt;For example, suppose the random delay is 15ms for Plugin A and 78ms for Plugin B. After 15ms, Plugin A, whose delay expired first, creates a &lt;code&gt;new YT.Player&lt;/code&gt;. Subsequently, when Plugin B performs its &lt;code&gt;YT.get()&lt;/code&gt; check after 78ms, the instance from Plugin A will already be detected. As a result, Plugin B cancels the creation of a new instance and safely shares the ride on Plugin A.&lt;/p&gt;

&lt;p&gt;The beauty of this design is that &lt;strong&gt;“if all plugins depending on the YouTube API adopt this ‘concession via random jitter’ design, conflict-free coexistence (ride-sharing) can be achieved autonomously in almost all cases.”&lt;/strong&gt; This is, so to speak, a piece of wisdom to achieve coexistence by writing the spirit of mutual concession into code without waiting for improvements on the API side.&lt;/p&gt;

&lt;p&gt;We have also built this collision avoidance and coexistence design using random jitter into the sticky plugin we developed, &lt;strong&gt;“KU Sticky Video for YouTube”&lt;/strong&gt; (and its paid Pro version).&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using the API of a major external service like YouTube in a plugin inevitably means harboring the potential for conflicts.&lt;/p&gt;

&lt;p&gt;Users embed YouTube videos in their posts and apply various customizations to them. For this, it is naturally expected that they will use multiple YouTube-related plugins together. In anticipation of this, designing an implementation that allows the dependent API to be used without conflicts is indispensable.&lt;/p&gt;

&lt;p&gt;The recently developed &lt;strong&gt;KU Sticky Video for YouTube&lt;/strong&gt; and its paid version, &lt;strong&gt;KU Sticky Video for YouTube Pro&lt;/strong&gt;, were also successfully released after identifying and resolving these issues one by one during development. If you want to showcase YouTube videos effectively alongside text, we highly recommend giving them a try.&lt;/p&gt;

&lt;p&gt;Please also try using them alongside other YouTube plugins! They should work without any problems (but if you do run into issues, please let me know quietly via the &lt;a href="https://karasunouta.com/en/contact/" rel="noopener noreferrer"&gt;contact form&lt;/a&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://karasunouta.com/en/wp-plugins/ku-sticky-video-for-youtube/" rel="noopener noreferrer"&gt;A WordPress plugin where the video player follows you (Demo &amp;amp; Info page)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://wordpress.org/plugins/ku-sticky-video-for-youtube/" rel="noopener noreferrer"&gt;KU Sticky Video for YouTube (WordPress.org official)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://karasunouta.com/en/store/ku-sticky-video-for-youtube-pro/" rel="noopener noreferrer"&gt;KU Sticky Video for YouTube Pro (Paid version store)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>wordpress</category>
      <category>javascript</category>
      <category>api</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
