创建 JavaScript 工具提示从未如此简单
本文演示了如何创建 JavaScript 工具提示。
引言
工具提示是网站用户界面中非常重要的一部分。它们提供了一种非常简单优雅的方式来提供有关网页元素的额外信息。但通常,当我们有这样的需求来为网页实现工具提示时,我们要么从头开始,要么使用现成的 JavaScript 工具提示库。本文并不是要指出哪种方法更好,在这里我将演示如何创建简单的工具提示,然后将它们发展成我们在网络上看到的更复杂、功能更丰富的酷炫工具提示。
Using the Code
工具提示就是 HTML 控件,我们在 HTML 元素的鼠标悬停和鼠标移出事件中显示和隐藏它们。我更喜欢为此目的使用一个 div
,这纯粹是您自己的选择。我尽可能地使用了 jQuery 来减少代码量并提高跨浏览器兼容性。
让我们从 JavaScript tooltip
的最基本实现开始,我们将不断改进这段代码,直到我们能够进行最终实现。
在下面的代码片段中,一个非常简单的工具提示被应用到一个购物车商品上。这段工具提示代码甚至不符合任何标准,但我们正在从头开始编写代码。
<body>
<div id= "Div1" style="width:220px;height:250px;">
<img src="images/db-apparel_The-Swede_white_large.jpg" />
</div>
<div id="divToolTip"
style="border:1px solid black;padding:5px;display:none;width:80px;height:50px;">
The Swede White, $29
</div>
<script type="text/javascript">
//called after window content is loaded.
function contentLoaded() {
//mouse-over event handler
$("#Div1").mouseover(function () {
$("#divToolTip").css("display", "inline");
});
//mouse-out event handler
$("#Div1").mouseout(function () {
$("#divToolTip").css("display", "none");
});
}
window.addEventListener("DOMContentLoaded", contentLoaded, false);
</script>
</body>
上面的代码非常容易实现。我们有绑定到 t-shirt div
的鼠标悬停和鼠标移出事件的函数,在鼠标悬停时,我们显示 tooltip div
,在鼠标移出时,我们隐藏它。这段代码在实现我们的工具提示方面再简单不过了,但输出有一些严重的缺点
- 工具提示缺乏适当的定位
- 静态代码
- 需要手动为工具提示添加一个 div
因此,让我们处理第一个问题,即正确放置我们的工具提示。以下代码将 tooltip
放置在源元素的右上角。
<body>
<div id= "Div1" style="width:220px;height:250px;">
<img src="images/db-apparel_The-Swede_white_large.jpg" />
</div>
<div id="divToolTip"
style="border:1px solid black;padding:5px;display:none;
width:80px;height:50px;position:absolute">
The Swede White, $29
</div>
<script type="text/javascript">
//called after window content is loaded.
function contentLoaded() {
//mouse-over event handler
$("#Div1").mouseover(function () {
var divToolTip = $("#divToolTip")
var sourceControl = $("#" + this.id);
var top = sourceControl.offset().top;
var right = sourceControl.offset().left + sourceControl.outerWidth();
divToolTip.css("top", top);
divToolTip.css("left", right);
divToolTip.css("display", "inline");
});
//mouse-out event handler
$("#Div1").mouseout(function () {
$("#divToolTip").css("display", "none");
});
}
window.addEventListener("DOMContentLoaded", contentLoaded, false);
</script>
</body>
在上面的代码中,jQuery offset()
方法获取元素相对于文档的当前坐标。因此,我们只是检索源元素的左右坐标,然后将我们的 div
放置在那些精确的坐标处(请记住将工具提示 div
的 css:position = 'absolute'
的值设置为)。
现在让我们看看这段代码中存在的问题
- 静态代码
- 静态位置
- 需要手动为工具提示添加一个 div
下一个代码片段消除了第 1 个和第 3 个问题。有些人甚至可能不认为它们是小型网站的问题,但是当应用程序扩展时,这种代码变得非常难以管理。
<body>
<div id= "Div1" style="width:220px;height:250px;">
<img src="images/db-apparel_Canonflex_large.jpg" />
</div>
<script type="text/javascript">
function ApplyToolTip(sourceControlId, content) {
var divToolTip = null;
var sourceControl = $("#" + sourceControlId);
divToolTip = $("#divToolTip");
if (!(divToolTip.length > 0)) {
divToolTip = document.createElement("div");
divToolTip.setAttribute("id", "divToolTip");
$("body").append(divToolTip);
divToolTip = $("#divToolTip");
divToolTip.css("position", "absolute");
divToolTip.css("display", "none");
}
divToolTip.css("border", "1px solid black");
divToolTip.css("padding", "5px");
//mouse-over event handler
$("#" + sourceControlId).mouseover(function () {
var top = sourceControl.offset().top;
var right = sourceControl.offset().left + sourceControl.outerWidth();
divToolTip.css("top", top);
divToolTip.css("left", right);
divToolTip.html(content);
divToolTip.css("display", "inline");
});
//mouse-out event handler
$("#" + sourceControlId).mouseout(function () {
$("#divToolTip").css("display", "none");
});
}
//called after window content is loaded.
function contentLoaded() {
ApplyToolTip("Div1", "1959, $29");
}
window.addEventListener("DOMContentLoaded", contentLoaded, false);
</script>
</body>
让我们看看上面的代码发生了什么
- 每当我们将工具提示应用于任何页面元素时,我们都会动态创建一个
div
。然后,每次我们想要显示任何页面元素的工具提示时,都会使用这个div
。 - 鼠标悬停和鼠标移出函数是从
ApplyToolTip
函数设置的。这使我们只需调用一个函数并传递所有必需的参数即可设置工具提示。
仍然存在的问题是
- 静态位置
我们的工具提示代码有一个缺点,它总是显示在右上角。现在假设我们的源元素右对齐,并且没有足够的空间来正确容纳工具提示,那么使用当前代码,工具提示将超出浏览器窗口的边界,结果会被裁剪。
为了解决这个问题,我们必须有条件地检查我们的工具提示在源元素和窗口边界之间是否有足够的空间显示。
<body>
<div id= "Div1" style="width:220px;height:250px;">
<img class= "productImage" src="images/db-apparel_Canonflex_large.jpg" />
</div>
<br /><br /><br /><br />
<div id= "Div2" style="width:220px;height:250px;">
<img src="images/db-apparel_Pentax6x7-seafoam_large.jpg" />
</div>
<script type="text/javascript">
function ApplyToolTip(sourceControlId, content) {
var divToolTip = null;
var sourceControl = $("#" + sourceControlId);
divToolTip = $("#divToolTip");
if (!(divToolTip.length > 0)) {
divToolTip = document.createElement("div");
divToolTip.setAttribute("id", "divToolTip");
$("body").append(divToolTip);
divToolTip = $("#divToolTip");
divToolTip.css("position", "absolute");
divToolTip.css("display", "none");
}
divToolTip.css("border", "1px solid black");
divToolTip.css("padding", "5px");
//mouse-over event handler
$("#" + sourceControlId).mouseover(function () {
var targetLeft = null, targetTop = null; //top and left of the tooltip div
var top = sourceControl.offset().top;
var left = sourceControl.offset().left;
var right = sourceControl.offset().left + sourceControl.outerWidth();
var bottom = sourceControl.offset().top + sourceControl.outerHeight();
divToolTip.html(content);
//check for suitable location to show the tooltip
//sequence is top>right>left>bottom
if (!(divToolTip.outerHeight() > top)) { //top
targetLeft = left;
//we need to set css left here to correctly compute
//the tooltip div height
divToolTip.css("left", targetLeft);
targetTop = top - divToolTip.outerHeight();
}
else if (!(divToolTip.outerWidth() > $(document).width() - right))
{ //right
targetLeft = right;
targetTop = top;
}
else if (!(divToolTip.outerWidth() > left)) { //left
targetLeft = left - divToolTip.outerWidth();
targetTop = top;
}
else if (!(divToolTip.outerHeight() > $(document).height() - bottom))
{ //bottom
targetLeft = left;
targetTop = bottom;
}
divToolTip.css("top", targetTop);
divToolTip.css("left", targetLeft);
divToolTip.css("display", "block");
});
//mouse-out event handler
$("#" + sourceControlId).mouseout(function () {
$("#divToolTip").css("display", "none");
});
}
//called after window content is loaded.
function contentLoaded() {
ApplyToolTip("Div1", "1959, $29");
ApplyToolTip("Div2", "Six By Seven, $29");
}
window.addEventListener("DOMContentLoaded", contentLoaded, false);
</script>
</body>
在上面的代码中,我们实现了一个根据空间可用性显示工具提示的序列。因此,我们不必担心工具提示在窗口边界处被裁剪。
我们的工具提示代码现在相当动态,但它包含在一个 script
标签中,所以如果我们要将此代码用于多个网页,那么我们必须将此代码复制到所有地方,这意味着大量的代码冗余。解决这个问题的方法是创建一个可以全局使用的单独模块。为此,我们只需将代码放入一个闭包中,并公开一个对象供全局使用。
此时我们应该转向最终版本,而不是查看更多代码片段,因为我们已经完成了基本功能的实现。在此之后,完全取决于你们添加自己喜欢的功能。所以让我们看看我添加了一些功能后实现的最终版本。
以下代码需要在一个单独的 JavaScript 文件中
;
(function (jQuery, w) {
var $ = jQuery;
var toolTipJS = function () {
//***Summary***
//array to hold tooltip location preferences
//*************
this.locationPreference = [];
//***Summary***
//Location object to be added to the location preference list
//*************
this.tooltipLocation = function (location, className) {
this.location = location;
this.className = className;
};
//***Summary***
//tooltip location constants
//*************
this.LocationConstants = {
Top: 1,
Left: 2,
Right: 3,
Bottom: 4
};
//***Summary***
//Add a location preference
//*************
this.addLocationPreference = function (l) {
this.locationPreference.push(l);
};
//***Summary***
//Resets location preferences
//*************
this.resetLocationPreference = function() {
this.locationPreference = [];
}
//***Summary***
//Flag to check if the mouse pointer is inside the source element
//*************
this.inside = false;
//***Summary***
//applies the tooltip show and hide functions on the mouseover and
//mouseout events of the source control
//***Params****
//sourceControlId = ID of source control.
//content = Tooltip content.
//distance = Distance between the tooltip and the source control.
//showAtPointer = Flag to determine if the tooltip will e stationary
//or will be moving with the mouse pointer
//*************
this.applyTooltip = function (sourceControlId, content, distance, showAtPointer) {
var divToolTip = null;
var showTooltipDelegate = null;
var hideTooltipDelegate = null;
var sourceControl = $("#" + sourceControlId);
var params = null;
divToolTip = $("#divToolTip");
//create our tooltip div if not already present
if (!(divToolTip.length > 0)) {
divToolTip = document.createElement("div");
divToolTip.setAttribute("id", "divToolTip");
$("body").append(divToolTip);
divToolTip = $("#divToolTip");
divToolTip.css("position", "absolute");
divToolTip.css("display", "none");
}
//delegate to change the calling context to our toolTipJS object
showTooltipDelegate = $.proxy(showToolTip, this);
hideTooltipDelegate = $.proxy(hideTooltip, this);
params = {
"sourceControl": sourceControl,
"content": content,
"distance": distance,
"showAtPointer": showAtPointer
}
if (showAtPointer === false) {
sourceControl.mouseover(params, showTooltipDelegate);
}
else {
sourceControl.mousemove(params, showTooltipDelegate);
}
sourceControl.mouseout(hideTooltipDelegate);
};
};
//***Summary***
//show the tooltip after computing the position and the correct style to apply on
//the tooltip div.
//*************
function showToolTip(e) {
var i = 0;
var showAtPointer = e.data.showAtPointer;
var sourceControl = e.data.sourceControl;
var content = e.data.content;
var targetLeft = null, targetTop = null; //top and left of the tooltip div
var top = sourceControl.offset().top;
var left = sourceControl.offset().left;
var right = sourceControl.offset().left + sourceControl.outerWidth();
var bottom = sourceControl.offset().top + sourceControl.outerHeight();
var divToolTip = $("#divToolTip");
var distance = e.data.distance;
if (showAtPointer === true) {
left = right = e.pageX;
top = bottom = e.pageY;
}
divToolTip.removeClass(); //remove any previous class
//reset top and left
if (this.inside === false) {
divToolTip.css("top", 0);
divToolTip.css("left", 0);
}
divToolTip.html(content); //set the tooltip content
for (; i < this.locationPreference.length; i++) {
switch (this.locationPreference[i].location) {
case this.LocationConstants.Top:
if (divToolTip.outerHeight() + distance > top) {
continue;
}
else {
//need to set the css here so as to retrieve
//final height after applying css
divToolTip.addClass(this.locationPreference[i].className);
targetLeft = left;
//we need to set css left here to correctly compute
//the tooltip div height
divToolTip.css("left", targetLeft);
targetTop = top - divToolTip.outerHeight() - distance;
}
break;
case this.LocationConstants.Right:
if ((divToolTip.outerWidth() + distance) > ($(window).width() - right)) {
continue;
}
else {
divToolTip.addClass(this.locationPreference[i].className);
targetLeft = right + distance;
targetTop = top;
}
break;
case this.LocationConstants.Left:
if (divToolTip.outerWidth() + distance > left) {
continue;
}
else {
//need to set the CSS here so as to retrieve final width
//after applying CSS
divToolTip.addClass(this.locationPreference[i].className);
targetLeft = left - divToolTip.outerWidth() - distance;
targetTop = top;
}
break;
case this.LocationConstants.Bottom:
if (divToolTip.outerHeight() + distance > $(window).height() - bottom) {
continue;
}
else {
divToolTip.addClass(this.locationPreference[i].className);
targetLeft = left;
targetTop = bottom + distance;
}
break;
}
break;
}
//apply the top and left for the tooltip div
divToolTip.css("top", targetTop);
divToolTip.css("left", targetLeft);
if (this.inside === false) {
divToolTip.css("display", "block");
this.inside = true;
}
};
//***Summary***
//hides the toooltip div.
//*************
function hideTooltip() {
this.inside = false;
$("#divToolTip").css("display", "none");
};
w["ToolTipJS"] = toolTipJS;
})($, window);
上述代码可以通过在网页中包含 JavaScript 文件来使用。此代码还有一些额外的补充,如下所示
- 创建一个暴露给窗口的单个对象,然后可以全局访问该对象,用于为不同的页面元素设置工具提示。
- 如果我们需要在源元素和工具提示 div 之间留出一些间隙,则添加一个“
distance
”参数。 - 添加一个位置偏好列表
locationPreference
,这样我们的工具提示就不必总是使用相同的序列位置在页面上显示。 - 添加了一个变体,其中工具提示随鼠标在元素上移动而不是显示在静态位置。
此代码可以很容易地修改,以在源元素周围添加更多自定义和动态的工具提示位置。以下是使用此代码的说明,更多文档可在该代码的 Github 页面上找到。
首先,我们需要为我们的工具提示添加位置偏好,为此您需要调用 TooltipJS.addLocationPreference
函数,并且建议您按照您的首选顺序添加所有可用的位置。其参数如下
addLocationPreference(location)
Location
:一个TooltipJS.tooltipLocation
类型的对象,包含位置常量和 CSS 类名。
TooltipJS.tooltipLocation
对象构造函数具有以下参数
location
:来自TooltipJS.LocationConstants
的值,表示单个位置。className
:当工具提示在该特定位置显示时应用的 CSS 类。
这是它的用法
tooltipJS.addLocationPreference(new tooltipJS.tooltipLocation
(tooltipJS.LocationConstants.Top, "tooltip-Top"));
之后,我们可以将工具提示应用于任何网页元素,为此,我们需要调用 ToolTipJS.applyTooltip
函数。以下是其参数及其描述
ToolTipJS.applyTooltip(sourceControlId, content, distance, showAtPointer)
sourceControlId
:我们要应用工具提示的源元素的 ID。content
:工具提示内容,可以是字符串或任何有效的 HTML 文本。distance
:源元素和工具提示之间的距离。如果showAtPointer
为true
,则这是当前鼠标位置和工具提示之间的距离。showAtPointer
:如果设置为true
,则tooltip
将随鼠标指针在源元素上移动而移动。
这是它的用法
tooltipJS.applyTooltip
("Div1", getProductContent("The Swede White", "29.00", "white"), 20, false);
tooltipJS.applyTooltip
("Div2", getProductContent("Six by Seven", "29.00", "#DAF4F0"), 20, true);
这里,getProductContent
是我用于将数据填充到模板中的函数,它不是 tooltip
对象的一部分。
您可以在 GitHub 页面上找到更多函数参考。
我从 这里 生成了气泡 CSS,因此除了提供的示例中的那些之外,您还可以应用一些很酷有趣的 CSS。
这些很酷的 T 恤图片来自 这里,万一您想买一件的话 。
浏览器支持
- Chrome:太棒了..!!
- Firefox: 太棒了..!!
- Internet Explorer: ???
结论
我创建这个库是为了非常特定的需求,但我确信它可以用于更多场景。如果您发现任何问题或错误,请随时在此处提供或在 GitHub 上添加。
历史
- 2013 年 9 月 13 日:初始版本