使用jQuery从HTML文本中查询脚本元素

5
我用$.ajax()加载页面,并将结果的部分插入到页面的相应位置:
$.ajax({
  url: '/whole_page.html',
  success: function (data, status, xhr) {
    $result = $(xhr.responseText);
    $('#menu').html($('#menu', $result).html());
    $('#content').html($('#content', $result).html());
  }
});

功能良好,但我遇到了一个问题。

现在我的页面包含一些特定于页面的 JS,必须在获取并插入结果时执行。据我所知,如果您从 HTML 文本构造 jQuery 对象并针对其查询,则 jQuery 不会返回 script 元素(我已经创建了一个演示 fiddle)。因此,我无法通过 jQuery 有选择地仅插入所需的脚本。

因此,似乎我必须手动从响应文本中提取 script 元素。基本上,我正在寻找正确的方法来做到这一点。以下是我迄今为止想到的:

function pullScripts(text) {
  var frag = document.createDocumentFragment()
    , div = document.createElement('div')
    , scriptElements
    ;

  // This is roughly how jQuery finds the scripts when cleaning text
  frag.appendChild(div);
  div.innerHTML = text;

  scriptElements = div.getElementsByClassName('class-for-scripts-that-we-want');

  $('head').append(scriptElements);
}

这个方法已经可以很好地工作,虽然我还没有在任何烂浏览器上测试过。不管怎样,为了避免jQuery的一个特性(特殊的script处理),复制这样基本的功能感觉不舒服。正如在fiddle中所看到的,jQuery对象实际上确实包含script,但是它不会像.html().get()那样返回它们。我是否错过了一些明显的方法来做到这一点? 注意:一旦jQuery的内部parseHTML函数被公开,整个主题都将无意义。 注意2:我也阅读了Trying to select script tags from a jQuery ajax get response,但是被接受的答案是“你不能”,随后是“使用正则表达式”。两者都不令人满意。

只是一点提醒,传递到成功回调函数中的第一个参数是请求返回的数据,而不是事件。 - Austin
$ .load() 地址是否解决了这个问题? - Christophe
@Austin,谢谢。我随手打的,知道我会搞砸它。Christophe:在某种程度上是这样,但如果您向URL添加选择器(如我需要使用它),则会遇到上述相同的问题。 - bkconrad
1
在jQuery 1.8.0b1中,您现在可以使用$.parseHTML()方法来简化此过程。 $.parseHTML(xhr.responseText,true)将为您提供包括脚本元素的jQuery对象。然后,您可以提取脚本标记并在附加HTML后进行追加或执行。如果升级jQuery不是一种选择,则可以将$.parseHTML的实现作为插件包含到当前的jQuery中。 - Kevin B
通常也可以使用正则表达式来获得相同的功能,就像你已经注意到的那样。这正是History.js在其示例之一中处理它的方式。请参见第134行及以下内容:https://gist.github.com/1145804 - Kevin B
1
$.parseHTML() 返回的是一组 DOM 节点数组,而不是 jQuery 对象。https://api.jquery.com/jQuery.parseHTML/ - rocky
3个回答

3
在 jQuery1.8.0b1 版本中,您现在可以使用 $.parseHTML() 方法来简化此过程。使用 $.parseHTML(xhr.responseText,true) 将为您提供一个包含脚本元素的jQuery对象。然后您可以提取脚本标记并在追加 HTML 后附加或执行它们。
$.ajax({
  url: '/whole_page.html',
  success: function (data, status, xhr) {
    var $result = $.parseHTML(xhr.responseText,true),
        $scripts = $result.find("script").add($result.filter("script")).detach();

    $('#menu').html($('#menu', $result).html());
    $('#content').html($('#content', $result).html());
    $('head').append($scripts);
  }
});

您可能需要针对不同情况筛选脚本以避免重复包含jQuery。这将取决于您正在加载的内容。

如果升级jQuery不是一个选择,您可以将$.parseHTML的实现作为插件包含到当前的jQuery中。

(function($) {
    if (!$.parseHTML) {
        var rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
        // data: string of html
        // context (optional): If specified, the fragment will be created in this context, defaults to document
        // scripts (optional): If true, will include scripts passed in the html string
        $.parseHTML = function( data, context, scripts ) {
            var parsed;
            if ( !data || typeof data !== "string" ) {
                return null;
            }
            if ( typeof context === "boolean" ) {
                scripts = context;
                context = 0;
            }
            context = context || document;

            // Single tag
            if ( (parsed = rsingleTag.exec( data )) ) {
                return [ context.createElement( parsed[1] ) ];
            }

            parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
            return jQuery.merge( [],
                (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
        }
    }
})(jQuery);

2

昨晚在jsFiddle上玩jQuery(edge)时,我无法在$.parseHTML()的结果上使用.find()等功能。问题在于$.parseHTML返回一个元素数组而不是jQuery对象,如果使用$()创建一个jQuery对象,你会回到原始的问题。所以,你仍然不能真正使用选择器。不过这并不是什么大问题:

$.ajax({
  url: '/some_page.html',
  success: function (data, text, xhr) {
    var result = $.parseHTML(data, true)
      , scripts = []
      , i;

    // filter the scripts somehow
    for (i = 0; i < result.length; i++) {
        if (result[i].getAttribute('class') === 'good') {
            scripts.push(result[i]);
        }
    }

    $('head').append(scripts);
  }
});​

我为此制作了一个可运行的代码片段


0
如前所述,您应该使用$.parseHTML来返回节点数组。
我已在脚本标签上添加了'append-on-ajax-script'类,该类应附加在ajax请求上。
此外,我在getAttribute('class')函数上遇到了一些问题,因此我的示例有一些解决方案来检查类别。
<script type="text/javascript" class="append-on-ajax-script">
$.post(
    $(this).prop('href'),
    {'some_data':'some_value'},
    function(data) {
        var script = $.parseHTML(data, true);
        for (var i = 0; i < script.length; i++) {
            //console.log(i, (' ' + script[i].className + ' ').indexOf(' append-on-ajax-script ') > -1 );
            if ((' ' + script[i].className + ' ').indexOf(' append-on-ajax-script ') > -1) {
                // console.log(i, script[i]);
                $('body').append(script[i]);
            }
        }

    }
);
</script>

<script type="text/javascript" class="append-on-ajax-script">
    alert('repeat me on ajax');
</script>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接