如何在跨iframe使用jQuery实例时修改查询上下文。

huangapple go评论69阅读模式
英文:

How to modify the query context when using a jQuery instance across iframes

问题

对于现有的 Web 应用程序1,我在跨 iframe 中使用了一个 jQuery 实例,这让我头痛不已。

jQuery 被加载(并别名为 $jq)在一个包含侧边栏 iframe 的文档中。在 iframe 文档中,我通过创建一个新的别名来重新使用父文档中的 jQuery,如 $jq = window.parent.$jq

问题是现在,iframe 中进行的查询是指向父文档的,即 $jq("body").attr("id") 返回父文档的 body 的 id,而不是 iframe 文档的 body 的 id。因此,我需要使用上下文参数进行查询,如 $jq("body", document).attr("id"),以获取当前执行 jQuery 的文档的 id。

我的问题是:我是否可以采取任何措施,以便不必在每个单独的查询中都添加上下文参数,以将查询限定在当前文档中?例如,我是否可以全局设置 jQuery 的上下文?

父文档

<!DOCTYPE html>
<html>
<head>
	<title>Parent</title>
	<style>body{ margin: 0; padding: 0; font-family: sans-serif;} #page{ display: flex; } #main { padding: 10px; flex: 1 0 auto; } #sidebar{ width: 360px; background-color: #fafafa; border: 0; padding: 10px; height: calc(100vh - 20px); }</style>
	<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
	<script>
		$jq = jQuery;
	</script>
</head>
<body id="Parent">
	<div id="page">
		<iframe id="sidebar" src="sidebar.html"></iframe>
	
		<div id="main">
			<h2>I am the parent</h2>
			<code class="result"></code>
		</div>
		
		<script>
			$('.result').html('$jq("body").attr("id"): <b>' + $jq("body").attr("id") + '</b>');
		</script>
	</div>
</body>
</html>

iframe 文档 (sidebar.html):

<!DOCTYPE html>
<html>
<head>
	<title>Sidebar</title>
	<style>body{font-family: sans-serif;}</style>
</head>
<body id="Sidebar">

	<h2>I am the sidebar iFrame</h2>
	<code class="result"></code>
	
	<script>
		var $jq = window.parent.$jq;
		var theId = $jq("body").attr("id");
		var theIdWithContext = $jq("body", document).attr("id");
		var el = document.querySelector('code');
		el.innerHTML += ('$jq("body").attr("id"): <b>' + theId + '</b><br><br>');
		el.innerHTML += ('$jq("body", document).attr("id"): <b>' + theIdWithContext + '</b>');
	</script>
	
</body>
</html>

这里是一个 测试案例,概述了我的问题。

1 其中使用了 jQuery,而替换它目前不在考虑范围内。

英文:

For an existing webapp<sup>1</sup>, i am using a jQuery instance across iframes which causes me headaches.

jQuery is loaded (and aliased to $jq) in a Document which contains an iFrame(for a sidebar). In the iFrame document, I reuse the jQuery from the parent document by creating a new alias in the iFrame like $jq = window.parent.$jq.

The problem is now, that queries made in the iFrame refer to the Parent-Document, i.e. $jq(&quot;body&quot;).attr(&quot;id&quot;) yields the id of the body of the parent document not the id of the body of the iframe-document, so I would have to query with the context parameter like $jq(&quot;body&quot;, document).attr(&quot;id&quot;) to get the id of the document jQuery is currently being executed in.

My Question is: can I do anything, so I would not have to add the context parameter to every single query in order to scope the queries to the current document? For example, can I set the context globally for jQuery or such?

Parent Document:

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
	&lt;title&gt;Parent&lt;/title&gt;
	&lt;style&gt;body{ margin: 0; padding: 0; font-family: sans-serif;} #page{ display: flex; } #main { padding: 10px; flex: 1 0 auto; } #sidebar{ width: 360px; background-color: #fafafa; border: 0; padding: 10px; height: calc(100vh - 20px); } &lt;/style&gt;
	&lt;script src=&quot;https://code.jquery.com/jquery-1.12.3.min.js&quot;&gt;&lt;/script&gt;
	&lt;script&gt;
		$jq = jQuery;
	&lt;/script&gt;
&lt;/head&gt;
&lt;body id=&quot;Parent&quot;&gt;
	&lt;div id=&quot;page&quot;&gt;
		&lt;iframe id=&quot;sidebar&quot; src=&quot;sidebar.html&quot;&gt;&lt;/iframe&gt;
	
		&lt;div id=&quot;main&quot;&gt;
			&lt;h2&gt;I am the parent&lt;/h2&gt;
			&lt;code class=&quot;result&quot;&gt;&lt;/code&gt;
		&lt;/main&gt;
	&lt;/div&gt;
	
	&lt;script&gt;
		$(&#39;.result&#39;).html(&#39;$jq(&quot;body&quot;).attr(&quot;id&quot;): &lt;b&gt;&#39; + $jq(&quot;body&quot;).attr(&quot;id&quot;) + &#39;&lt;/b&gt;&#39;);
	&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

iFrame Document (sidebar.html):

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
	&lt;title&gt;Sidebar&lt;/title&gt;
	&lt;style&gt;body{font-family: sans-serif;}&lt;/style&gt;
&lt;/head&gt;
&lt;body id=&quot;Sidebar&quot;&gt;

	&lt;h2&gt;I am the sidebar iFrame&lt;/h2&gt;
	&lt;code class=&quot;result&quot;&gt;&lt;/code&gt;
	
	&lt;script&gt;
		var $jq = window.parent.$jq;
		var theId = $jq(&quot;body&quot;).attr(&quot;id&quot;);
		var theIdithContext = $jq(&quot;body&quot;, document).attr(&quot;id&quot;);
		var el = document.querySelector(&#39;code&#39;);
		el.innerHTML +=(&#39;$jq(&quot;body&quot;).attr(&quot;id&quot;): &lt;b&gt;&#39; + theId + &#39;&lt;/b&gt;&lt;br&gt;&lt;br&gt;&#39;);
		el.innerHTML +=(&#39;$jq(&quot;body&quot;, document).attr(&quot;id&quot;): &lt;b&gt;&#39; + theIdithContext + &#39;&lt;/b&gt;&#39;);
		
	&lt;/script&gt;
	
&lt;/body&gt;
&lt;/html&gt;

And here a testcase outlining my problem.

<hr>

<sup>1</sup> which happens to use jQuery and replacing that is out of scope for now

答案1

得分: 1

sidebar.html中创建一个在你需要的上下文中运行的函数。同时,你也可以不分配$jq = jQuery;

在下面的代码中,jQuery将具有父文档上下文,而$将具有iFrame文档上下文。你可以为这些变量分配任何名称。

const jQuery = window.parent.$;
const $ = (selector) => jQuery(selector, document);
Object.setPrototypeOf( $, jQuery );

示例


父文档:

<!DOCTYPE html>
<html>
<head>
  <title>Parent</title>
  <style>body{ margin: 0; padding: 0; font-family: sans-serif;} #page{ display: flex; }</style>
  <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
</head>
<body id="Parent">
  <div id="page">
    <iframe id="sidebar" src="sidebar.html"></iframe>
    <div id="main">
      <h2>I am the parent</h2>
      <code class="result"></code>
    </div>
  </div>
  <script>$('.result').html(`bodyID: <b>${$('body').attr('id')}</b>`);</script>
</body>
</html>

iFrame文档:

<!DOCTYPE html>
<html>
<head>
  <title>Sidebar</title>
  <style>body{font-family: sans-serif;}</style>
</head>
<body id="Sidebar">
  <h2>I am the sidebar iFrame</h2>
  <code class="result"></code>
  <script>
    const jQuery = window.parent.$;
    const $ = (selector) => jQuery(selector, document);
    Object.setPrototypeOf( $, jQuery );
    const bodyID = $('body').attr('id');
    const parentBodyID = jQuery('body').attr('id');
    const el = document.querySelector('code');
    $('code').html(`bodyID: <b>${bodyID}</b><br>parentBodyID: <b>${parentBodyID}</b>`);
  </script>
</body>
</html>
英文:

Just create a function in sidebar.html that will work in the context you need.
And you can also not assign $jq = jQuery;.

In the code below, jQuery will have a Parent Document context and $ will have an iFrame Document context. (You can assign any names to the variables).

const jQuery = window.parent.$;
const $ = (selector) =&gt; jQuery(selector, document);
Object.setPrototypeOf( $, jQuery );

Example


Parent Document:

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Parent&lt;/title&gt;
  &lt;style&gt;body{ margin: 0; padding: 0; font-family: sans-serif;} #page{ display: flex; }&lt;/style&gt;
  &lt;script src=&quot;https://code.jquery.com/jquery-3.7.0.min.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body id=&quot;Parent&quot;&gt;
  &lt;div id=&quot;page&quot;&gt;
    &lt;iframe id=&quot;sidebar&quot; src=&quot;sidebar.html&quot;&gt;&lt;/iframe&gt;
    &lt;div id=&quot;main&quot;&gt;
      &lt;h2&gt;I am the parent&lt;/h2&gt;
      &lt;code class=&quot;result&quot;&gt;&lt;/code&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;script&gt;$(&#39;.result&#39;).html(`bodyID: &lt;b&gt;${$(&#39;body&#39;).attr(&#39;id&#39;)}&lt;/b&gt;`);&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

<!-- end snippet -->


iFrame Document:

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Sidebar&lt;/title&gt;
  &lt;style&gt;body{font-family: sans-serif;}&lt;/style&gt;
&lt;/head&gt;
&lt;body id=&quot;Sidebar&quot;&gt;
  &lt;h2&gt;I am the sidebar iFrame&lt;/h2&gt;
  &lt;code class=&quot;result&quot;&gt;&lt;/code&gt;
  &lt;script&gt;
    const jQuery = window.parent.$;
    const $ = (selector) =&gt; jQuery(selector, document);
    Object.setPrototypeOf( $, jQuery );
    const bodyID = $(&#39;body&#39;).attr(&#39;id&#39;);
    const parentBodyID = jQuery(&#39;body&#39;).attr(&#39;id&#39;);
    const el = document.querySelector(&#39;code&#39;);
    $(&#39;code&#39;).html(`bodyID: &lt;b&gt;${bodyID}&lt;/b&gt;&lt;br&gt;parentBodyID: &lt;b&gt;${parentBodyID}&lt;/b&gt;`);
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

<!-- end snippet -->


答案2

得分: 1

简短回答:这是不可能的,较长回答:这是不需要的。

我认为整个问题被过度设计,过于追求完美和DRY原则。你可以轻松地重用父级的jQuery对象来获取你的侧边栏的主体并与之交互:

const $body = window.parent.$(document.body);

但是为什么需要在iframe中包含你的脚本呢?这也似乎是不必要的。你可以轻松地从父级操作你的iframe:

const $iframeBody = $($('iframe')[0].contentDocument.body);
$iframeBody.append("<div>added by parent</div>");

此外,我认为在所有地方使用$(selector) 作为jQuery代码质量较低,因为它缺乏容器上下文。

回到问题本身。

首先,如果你查看jQuery的压缩代码,你会发现它是一个硬对象,这意味着窗口和文档成为脚本执行时定义的私有作用域变量:

/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */
// ...

所以你不能像改变原型那样来改变窗口和文档。

其次,要为你的iframe创建一个jQuery对象,你只需要在你的iframe中再次执行这个脚本。所以将jQuery放入你的iframe的最简单方法如下:

<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>

现代浏览器足够聪明,不会重复下载脚本。因此,你在父页面和iframe中都有了相同的jQuery,只需要一个源代码文件。基本上,浏览器只下载一次jQuery的源代码,然后为父页面和iframe页面创建单独的jQuery对象。
我认为这非常简单,不需要更复杂的解决方案。

你可以在这里查看代码示例:
https://codesandbox.io/s/silly-ardinghelli-v7774q?file=/src/frame.js

在iframe的JS中,我可以轻松地同时使用父级的jQuery和iframe的jQuery:

$jp = window.parent.jQuery;
$(() => {
  $("body").append("<div>added by iframe</div>");
  $jp("body").append("<div>added by iframe</div>");
});

结果:
如何在跨iframe使用jQuery实例时修改查询上下文。

英文:

A short answer: it's impossible, a longer answer: it's not needed.

I think the whole problem is overengineered, too much perfection & DRY. you can easily reuse the parent's jQuery object to get your sidebar's body and work with it:

const $body = window.parent.$(document.body);

But why you need to include your script in the iframe? Also seems unnecessary. You can easily manipulate your iframe from the parent:

const $iframeBody = $($(&#39;iframe&#39;)[0].contentDocument.body);
$iframeBody.append(&quot;&lt;div&gt;added by parent&lt;/div&gt;&quot;);

Also I consider using $(selector) everywhere as low quality jQuery spaghetti code since it's lacking any container context.

Back to the problem.

First if you look at jQuery's minified code you'll find it's a hard object which means the window & document become private scope variables defined at the script execution:

/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(e, t) {
    &quot;use strict&quot;;
    &quot;object&quot; == typeof module &amp;&amp; &quot;object&quot; == typeof module.exports ? module.exports = e.document ? t(e, !0) : function(e) {
        if (!e.document)
            throw new Error(&quot;jQuery requires a window with a document&quot;);
        return t(e)
    }
    : t(e)
}(&quot;undefined&quot; != typeof window ? window : this, function(e, t) {
    &quot;use strict&quot;;
    var n = []
      , r = e.document
      , i = Object.getPrototypeOf
      , o = n.slice
      , a = n.concat
      , s = n.push
      , u = n.indexOf
      , l = {}
      , c = l.toString
      , f = l.hasOwnProperty
      , p = f.toString
      , d = p.call(Object)
      , h = {}

So you can't change something like prototype to change window & document.

Second to create a jQuery object for your iframe you just need to execute this script again in your iframe. So the easiest why to put jQuery into your frame:

&lt;script src=&quot;https://code.jquery.com/jquery-1.12.3.min.js&quot;&gt;&lt;/script&gt;

Modern browsers are smart enough not to download the script twice.
So you have your jQuery in the parent page and in the iframe with one source code file. Basically the browser download the jQuery's source once than creates individual jQuery objects for your parent and iframe pages.
I think it's really easy and doesn't require any smarter solution.

The gist could be seen here:
https://codesandbox.io/s/silly-ardinghelli-v7774q?file=/src/frame.js

In the iframe's JS I just easily can use both the parent's jQuery and the iframe's one:

$jp = window.parent.jQuery;
$(() =&gt; {
  $(&quot;body&quot;).append(&quot;&lt;div&gt;added by iframe&lt;/div&gt;&quot;);
  $jp(&quot;body&quot;).append(&quot;&lt;div&gt;added by iframe&lt;/div&gt;&quot;);
});

The result:
如何在跨iframe使用jQuery实例时修改查询上下文。

答案3

得分: 0

您可以将$jq定义为一个以selector作为属性并调用window.parent.$jq(selector, document)function

var $jq = function(selector) {
    return window.parent.$jq(selector, document);
};

完整的iframe代码:

<!DOCTYPE html>
<html>
<head>
    <title>Sidebar</title>
    <style>body{font-family: sans-serif;}</style>
</head>
<body id="Sidebar">

    <h2>I am the sidebar iFrame</h2>
    <code class="result"></code>
    
    <script>
        var $jq = function(selector) {
            return window.parent.$jq(selector, document);
        };
        var theId = $jq("body").attr("id");
        var theIdithContext = $jq("body", document).attr("id");
        var el = document.querySelector('code');
        el.innerHTML +=('$jq("body").attr("id"): <b>' + theId + '</b><br><br>');
        el.innerHTML +=('$jq("body", document).attr("id"): <b>' + theIdithContext + '</b>');
        
    </script>
    
</body>
</html>

成功的测试:

如何在跨iframe使用jQuery实例时修改查询上下文。

英文:

You can define $jq as being a function that takes a selector as an attribute and calls window.parent.$jq(selector, document):

        var $jq = function(selector) {
            return window.parent.$jq(selector, document);
        };

Full iframe code:

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Sidebar&lt;/title&gt;
    &lt;style&gt;body{font-family: sans-serif;}&lt;/style&gt;
&lt;/head&gt;
&lt;body id=&quot;Sidebar&quot;&gt;

    &lt;h2&gt;I am the sidebar iFrame&lt;/h2&gt;
    &lt;code class=&quot;result&quot;&gt;&lt;/code&gt;
    
    &lt;script&gt;
        var $jq = function(selector) {
            return window.parent.$jq(selector, document);
        };
        var theId = $jq(&quot;body&quot;).attr(&quot;id&quot;);
        var theIdithContext = $jq(&quot;body&quot;, document).attr(&quot;id&quot;);
        var el = document.querySelector(&#39;code&#39;);
        el.innerHTML +=(&#39;$jq(&quot;body&quot;).attr(&quot;id&quot;): &lt;b&gt;&#39; + theId + &#39;&lt;/b&gt;&lt;br&gt;&lt;br&gt;&#39;);
        el.innerHTML +=(&#39;$jq(&quot;body&quot;, document).attr(&quot;id&quot;): &lt;b&gt;&#39; + theIdithContext + &#39;&lt;/b&gt;&#39;);
        
    &lt;/script&gt;
    
&lt;/body&gt;
&lt;/html&gt;

Successful test:

如何在跨iframe使用jQuery实例时修改查询上下文。

huangapple
  • 本文由 发表于 2023年6月1日 19:34:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76381438.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定