jQuery 工具提示不适用于动态加载的 HTML 内容
jQuery tooltip not working on dynamically loaded HTML content
站点由 3 个页面组成,index.html、1.html、2.html。
目标是从包含工具提示和其他一些 html 的 1.html 加载 html 并将其显示在 div 中,鼠标悬停在加载的工具提示上后,我希望工具提示文本从 2.html.
中获取
但是此代码不起作用,未显示任何错误并且 AJAX 从未执行。
$(document).ready(function(){
$( ".content span" ).tooltip({
track:true,
open: function( event, ui ) {
var id = this.id;
var userid = $(this).attr('data-id');
$.ajax({
url:'2.html',
type:'post',
data:{userid:userid},
success: function(response){
$("#"+id).tooltip('option','content',response);
}
});
}
});
});
.container{
margin: 0 auto;
width: 30%;
}
.content{
border: 1px solid black;
padding: 5px;
margin-bottom: 5px;
}
.content span{
width: 250px;
}
.content span:hover{
cursor: pointer;
}
<!doctype html>
<html>
<head>
<title>Dynamically show data in the Tooltip using AJAX</title>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
</head>
<body>
<input type="button" id="btnLoad" value="Load"/>
<div class='container' id="container"></div>
</body>
<script>
$( "#btnLoad" ).click(function() {
$.ajax({
url: "1.html",
context: document.body
}).done(function( msg ) {
$('#container').html(msg);
$(this).tooltip();
});
});
</script>
</html>
1.html 文件的样本 HTML
<div class='content'>
<span title='Please wait..' data-id='1' id='user_1'>Pepsi</span>
</div>
<div class='content'>
<span title='Please wait..' data-id='2' id='user_2'>7 Up</span>
</div>
2.html 文件的样本 HTML
This Tooltip text will be replaced with texts from Database
你能尝试将这一行推送到你的代码中吗?
$('body').tooltip({selector: '.content span'});
紧接着
$(document).ready(function() {
// push the above line here
... your current code
}
已更新
您更新后的问题更加清晰,并帮助我追踪发生了什么。我原来的回答解决了其中一个问题,但不是全部。其实还有一系列的问题。
问题 1
第一个问题是我之前描述的问题:jQuery 选择器仅匹配页面加载时 存在的页面元素 。 $(".content span").tooltip()
只会在页面加载时初始化存在于 .content
中的 span
的工具提示。如果您稍后注入新的 span
s(或内部带有 span
s 的新 .content
s),它们将不包括在内,并且不会有工具提示。
最简单的修复方法是使用 (apparently undocumented?) selector
工具提示选项。在页面加载 时存在的 HTML 元素 上初始化工具提示,然后使用 selector
选项进行过滤以仅匹配您真正想要具有工具提示的元素在。关键是过滤部分是实时发生的,所以会匹配新添加的元素。
对于您的情况,我们不能使用 .content
作为工具提示,因为它在页面加载时也不存在。在您的任何 AJAX 事情发生之前,我们需要一些 parent 元素存在于页面上。假设您有一个 parent,例如 <div class="parent">
,并且您的 HTML 将被注入其中(我想您也可以使用 body
或 document
)。所以我们在 parent div:
上初始化工具提示
$(".parent").tooltip({
// the selector option filters the elements that will actually have tooltips
selector: '.content span'
});
问题 2
现在我们可以使用基本的工具提示功能,即使对于注入到页面中的新内容也是如此。下一步是通过 AJAX 动态加载内容,并使用该内容更新工具提示的标题。通常你会这样做:
$(selector).tooltip('option', 'content', 'New tooltip text!');
其中 selector
与 individual span
匹配,我们目前正在查看其工具提示。但在我们的例子中,这不起作用 - span
没有附加工具提示! 因为我们正在 .parent
上初始化工具提示,所以 individual span
s 显示工具提示的地方没有附加工具提示,调用 .tooltip()
将失败并出现如下错误:
cannot call methods on tooltip prior to initialization; attempted to call method 'option'
要引用现有的工具提示,我们需要做 $('.parent').tooltip()
,但如果我们这样做:
// Don't do this
$('.parent').tooltip('option', 'content', 'New tooltip text!');
它会将页面上的 每个 工具提示更改为相同的 'New tooltip text!'
,我们不希望这样。
您用来触发 AJAX receives the jQuery event
as a parameter 的 .open
事件处理程序。 event
包含有关当前目标元素的信息。使用它,我们可以找到我们当前正在查看的页面上的哪个跨度:
open: function(event, ui) {
// Find real targeted tooltip
let target = $(event.originalEvent.target);
现在我们可以操作该元素的标题了:
$target.attr('title', 'New tooltip!');
我们也可以把它做成工具提示!
$target.tooltip();
实际上这还不够,因为 .parent
工具提示已经打开并显示“请稍候...”。上述步骤确实更新了标题,但您必须将鼠标移开然后再移回才能看到它。我们希望内容立即刷新,实时。我尝试了一些实验,发现效果很好:
// Temporarily disabling the parent tooltip hides the one that was already open
$('.parent').tooltip('disable');
// Update our target's title
$target.attr('title', 'New tooltip!');
// Initialise a tooltip on our target, and immediately open it
$target.tooltip().tooltip('open');
// Re-enable the parent tooltips, so this process will work again for other spans
$('.parent').tooltip('enable');
所以我们实际上是在页面中添加新的工具提示实例,每次我们 mouse-over 一个匹配的范围。这种方法具有额外的优势,即新的工具提示取代了旧的 parent 特定 span
的工具提示。这意味着 parent 工具提示选项不再适用于已加载其工具提示内容的 span
,这意味着如果您第二次 mouse-over 它,它不会触发 AJAX 第二次请求 span
。我们从第一个 AJAX 调用中获得的内容已经存在,只是 re-used.
这是演示所有这些的工作片段。
我使用了 https://www.npoint.io/ 这是一个 JSON 存储箱,来模拟您的 AJAX 呼叫。我与它没有任何关系,它看起来只是一个方便的工具。不幸的是,它似乎不太可靠,并且在我工作时对它的请求失败了几次。如果发生这种情况,请重试。
我添加了一些包含与您的 1.html
和 2.html
文件匹配的数据的容器,下面的代码加载它们并使用它们的内容:
- This bin 包含您的 HTML 内容;
- This bin 包含用户 ID 0 的虚拟数据;
- This bin 包含用户 ID 1 的虚拟数据;
- This bin 包含用户 ID 2 的虚拟数据;
$(document).ready(function () {
// This URL returns a JSON response including HTML like your 1.html,
// this way we can realistically simulate your 1.html AJAX call.
let htmlContentUrl = 'https://api.npoint.io/6893d2fd7632835742cf';
// These URLs return JSON responses including plain text like your
// 2.html file, this way we can realistically simulate AJAX calls
// loading and using data from your DB
let userContentUrls = [
'https://api.npoint.io/06cf752b395286e41cb4',
'https://api.npoint.io/2a3e0a338f92a803b3fc',
'https://api.npoint.io/a9a4296244c36e8d5bfc'
];
// We use this selector a few times, it is good practice to cache it
var $parent = $('.parent');
$("#btnLoad").click(function () {
$.ajax({
url: htmlContentUrl,
}).done(function (msg) {
$('#container').html(msg.content);
});
});
$parent.tooltip({
selector: '.conten span',
track: true,
open: function (event, ui) {
// Find real targetted tooltip
var $target = $(event.originalEvent.target);
var userid = $target.attr('data-id');
url = userContentUrls[userid];
console.log('For userid', userid, 'url is', url);
$.ajax({
url: url,
type: 'get',
data: {
userid: userid
},
success: function (response) {
// We can't simply update the tooltip for $target, because
// it *has* no tooltip! The tooltip is actually attached
// to .parent. To refresh the already visible tooltip, we
// need to take a few steps.
$parent.tooltip('disable');
$target.attr('title', response.content);
$target.tooltip().tooltip('open');
$parent.tooltip('enable');
}
});
}
});
});
.container {
margin: 0 auto;
width: 30%;
}
.content {
border: 1px solid black;
padding: 5px;
margin-bottom: 5px;
}
.content span {
width: 250px;
}
.content span:hover {
cursor: pointer;
}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="parent">
<div class="container">
<div class="content">
<span id="user_0" title="Please wait ..." data-id="0">Water</span>
</div>
</div>
<input type="button" id="btnLoad" value="Load" />
<div class='container' id="container"></div>
</div>
旁注:
我添加了一个在页面加载时可见的工具提示(用户 ID 0),只是为了演示在页面加载时和注入新内容后一切正常;
Convention是获取数据时使用GET,更改数据时使用POST。您的 AJAX 调用只是检索要显示的数据,所以实际上应该是 GET;
我不确定你为什么要 context: document.body
,但我认为没有必要,为了简单起见,我删除了它;
如果您要多次使用 jQuery 选择器,it makes sense to cache them。我为 $parent
和 $target
.
这样做了
站点由 3 个页面组成,index.html、1.html、2.html。
目标是从包含工具提示和其他一些 html 的 1.html 加载 html 并将其显示在 div 中,鼠标悬停在加载的工具提示上后,我希望工具提示文本从 2.html.
中获取
但是此代码不起作用,未显示任何错误并且 AJAX 从未执行。
$(document).ready(function(){
$( ".content span" ).tooltip({
track:true,
open: function( event, ui ) {
var id = this.id;
var userid = $(this).attr('data-id');
$.ajax({
url:'2.html',
type:'post',
data:{userid:userid},
success: function(response){
$("#"+id).tooltip('option','content',response);
}
});
}
});
});
.container{
margin: 0 auto;
width: 30%;
}
.content{
border: 1px solid black;
padding: 5px;
margin-bottom: 5px;
}
.content span{
width: 250px;
}
.content span:hover{
cursor: pointer;
}
<!doctype html>
<html>
<head>
<title>Dynamically show data in the Tooltip using AJAX</title>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
</head>
<body>
<input type="button" id="btnLoad" value="Load"/>
<div class='container' id="container"></div>
</body>
<script>
$( "#btnLoad" ).click(function() {
$.ajax({
url: "1.html",
context: document.body
}).done(function( msg ) {
$('#container').html(msg);
$(this).tooltip();
});
});
</script>
</html>
1.html 文件的样本 HTML
<div class='content'>
<span title='Please wait..' data-id='1' id='user_1'>Pepsi</span>
</div>
<div class='content'>
<span title='Please wait..' data-id='2' id='user_2'>7 Up</span>
</div>
2.html 文件的样本 HTML
This Tooltip text will be replaced with texts from Database
你能尝试将这一行推送到你的代码中吗?
$('body').tooltip({selector: '.content span'});
紧接着
$(document).ready(function() {
// push the above line here
... your current code
}
已更新
您更新后的问题更加清晰,并帮助我追踪发生了什么。我原来的回答解决了其中一个问题,但不是全部。其实还有一系列的问题。
问题 1
第一个问题是我之前描述的问题:jQuery 选择器仅匹配页面加载时 存在的页面元素 。 $(".content span").tooltip()
只会在页面加载时初始化存在于 .content
中的 span
的工具提示。如果您稍后注入新的 span
s(或内部带有 span
s 的新 .content
s),它们将不包括在内,并且不会有工具提示。
最简单的修复方法是使用 (apparently undocumented?) selector
工具提示选项。在页面加载 时存在的 HTML 元素 上初始化工具提示,然后使用 selector
选项进行过滤以仅匹配您真正想要具有工具提示的元素在。关键是过滤部分是实时发生的,所以会匹配新添加的元素。
对于您的情况,我们不能使用 .content
作为工具提示,因为它在页面加载时也不存在。在您的任何 AJAX 事情发生之前,我们需要一些 parent 元素存在于页面上。假设您有一个 parent,例如 <div class="parent">
,并且您的 HTML 将被注入其中(我想您也可以使用 body
或 document
)。所以我们在 parent div:
$(".parent").tooltip({
// the selector option filters the elements that will actually have tooltips
selector: '.content span'
});
问题 2
现在我们可以使用基本的工具提示功能,即使对于注入到页面中的新内容也是如此。下一步是通过 AJAX 动态加载内容,并使用该内容更新工具提示的标题。通常你会这样做:
$(selector).tooltip('option', 'content', 'New tooltip text!');
其中 selector
与 individual span
匹配,我们目前正在查看其工具提示。但在我们的例子中,这不起作用 - span
没有附加工具提示! 因为我们正在 .parent
上初始化工具提示,所以 individual span
s 显示工具提示的地方没有附加工具提示,调用 .tooltip()
将失败并出现如下错误:
cannot call methods on tooltip prior to initialization; attempted to call method 'option'
要引用现有的工具提示,我们需要做 $('.parent').tooltip()
,但如果我们这样做:
// Don't do this
$('.parent').tooltip('option', 'content', 'New tooltip text!');
它会将页面上的 每个 工具提示更改为相同的 'New tooltip text!'
,我们不希望这样。
您用来触发 AJAX receives the jQuery event
as a parameter 的 .open
事件处理程序。 event
包含有关当前目标元素的信息。使用它,我们可以找到我们当前正在查看的页面上的哪个跨度:
open: function(event, ui) {
// Find real targeted tooltip
let target = $(event.originalEvent.target);
现在我们可以操作该元素的标题了:
$target.attr('title', 'New tooltip!');
我们也可以把它做成工具提示!
$target.tooltip();
实际上这还不够,因为 .parent
工具提示已经打开并显示“请稍候...”。上述步骤确实更新了标题,但您必须将鼠标移开然后再移回才能看到它。我们希望内容立即刷新,实时。我尝试了一些实验,发现效果很好:
// Temporarily disabling the parent tooltip hides the one that was already open
$('.parent').tooltip('disable');
// Update our target's title
$target.attr('title', 'New tooltip!');
// Initialise a tooltip on our target, and immediately open it
$target.tooltip().tooltip('open');
// Re-enable the parent tooltips, so this process will work again for other spans
$('.parent').tooltip('enable');
所以我们实际上是在页面中添加新的工具提示实例,每次我们 mouse-over 一个匹配的范围。这种方法具有额外的优势,即新的工具提示取代了旧的 parent 特定 span
的工具提示。这意味着 parent 工具提示选项不再适用于已加载其工具提示内容的 span
,这意味着如果您第二次 mouse-over 它,它不会触发 AJAX 第二次请求 span
。我们从第一个 AJAX 调用中获得的内容已经存在,只是 re-used.
这是演示所有这些的工作片段。
我使用了 https://www.npoint.io/ 这是一个 JSON 存储箱,来模拟您的 AJAX 呼叫。我与它没有任何关系,它看起来只是一个方便的工具。不幸的是,它似乎不太可靠,并且在我工作时对它的请求失败了几次。如果发生这种情况,请重试。
我添加了一些包含与您的 1.html
和 2.html
文件匹配的数据的容器,下面的代码加载它们并使用它们的内容:
- This bin 包含您的 HTML 内容;
- This bin 包含用户 ID 0 的虚拟数据;
- This bin 包含用户 ID 1 的虚拟数据;
- This bin 包含用户 ID 2 的虚拟数据;
$(document).ready(function () {
// This URL returns a JSON response including HTML like your 1.html,
// this way we can realistically simulate your 1.html AJAX call.
let htmlContentUrl = 'https://api.npoint.io/6893d2fd7632835742cf';
// These URLs return JSON responses including plain text like your
// 2.html file, this way we can realistically simulate AJAX calls
// loading and using data from your DB
let userContentUrls = [
'https://api.npoint.io/06cf752b395286e41cb4',
'https://api.npoint.io/2a3e0a338f92a803b3fc',
'https://api.npoint.io/a9a4296244c36e8d5bfc'
];
// We use this selector a few times, it is good practice to cache it
var $parent = $('.parent');
$("#btnLoad").click(function () {
$.ajax({
url: htmlContentUrl,
}).done(function (msg) {
$('#container').html(msg.content);
});
});
$parent.tooltip({
selector: '.conten span',
track: true,
open: function (event, ui) {
// Find real targetted tooltip
var $target = $(event.originalEvent.target);
var userid = $target.attr('data-id');
url = userContentUrls[userid];
console.log('For userid', userid, 'url is', url);
$.ajax({
url: url,
type: 'get',
data: {
userid: userid
},
success: function (response) {
// We can't simply update the tooltip for $target, because
// it *has* no tooltip! The tooltip is actually attached
// to .parent. To refresh the already visible tooltip, we
// need to take a few steps.
$parent.tooltip('disable');
$target.attr('title', response.content);
$target.tooltip().tooltip('open');
$parent.tooltip('enable');
}
});
}
});
});
.container {
margin: 0 auto;
width: 30%;
}
.content {
border: 1px solid black;
padding: 5px;
margin-bottom: 5px;
}
.content span {
width: 250px;
}
.content span:hover {
cursor: pointer;
}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="parent">
<div class="container">
<div class="content">
<span id="user_0" title="Please wait ..." data-id="0">Water</span>
</div>
</div>
<input type="button" id="btnLoad" value="Load" />
<div class='container' id="container"></div>
</div>
旁注:
我添加了一个在页面加载时可见的工具提示(用户 ID 0),只是为了演示在页面加载时和注入新内容后一切正常;
Convention是获取数据时使用GET,更改数据时使用POST。您的 AJAX 调用只是检索要显示的数据,所以实际上应该是 GET;
我不确定你为什么要
context: document.body
,但我认为没有必要,为了简单起见,我删除了它;如果您要多次使用 jQuery 选择器,it makes sense to cache them。我为
这样做了$parent
和$target
.