工具提示:[data-tooltip-content] 作为指向动态 HTML 的指针
Tooltipster: [data-tooltip-content] as pointer to dynamic HTML
THE ISSUE: Using Tooltipster.JS with FullCalendar and / or dynamically rendered content
我已经抓耳挠腮琢磨了好一阵子了,但我离开始时的状态还很远。我已经阅读并重新阅读了 Tooltipster 文档,但我似乎无法使用 'data-tooltip-content'
作为指向 #idTooltipsterElement
的指针。它只是显示 'data-tooltip-content' 的 value,即使文档明确指出:
工具提示文档指出:
5. Use HTML inside your tooltips
Tooltipster allows you to use any HTML markup inside your tooltips. It
means that you can insert things like images and text formatting tags.
Instead of a title attribute, use a data-tooltip-content attribute to
provide a selector that corresponds to the HTML element of your page
that should be used as content. This is your HTML:
<span class="tooltip" data-tooltip-content="#tooltip_content">This span has a tooltip with HTML when you hover over it!</span>
<div class="tooltip_templates">
<span id="tooltip_content">
<img src="myimage.png" /> <strong>This is the content of my tooltip!</strong>
</span>
</div>
In your CSS file, add .tooltip_templates { display: none; }
so that
the content does not get displayed outside of the tooltip.
当我们从 Tippy.js 移动到 Tooltipster.js 时,我遇到了其他问题,谢天谢地,这些问题不存在在 Tootipster 中,但我发现前者更容易设置,尽管它的功能和文档数量远不及后者。
但是:我之前只是使用 'data-tooltip-content'
将我的整个 HTML 元素放入其中,奇怪的是我注意到 这也适用于 Tooltipster 作为好吧,即使他们的文档似乎更喜欢上述方法。现在通常我会简单地保留我已经熟悉的旧方法,但不幸的是,这带来了一整套 与 post 事件渲染更改相关的潜在挑战(我们可能会在我们的页面上广泛使用的 FullCalendar 中实施),以及我已经经历过的 formatting / styling difficulties。
我有理由相信记录在案的 Tooltipster 中描述的 首选解决方案 将是我们实现的理想选择,但我只是无法弄清楚如何让它在我们的环境中工作fullCalendar环境分解为以下程序流程:
eventRender:从事件源中提取事件并渲染
所选月份/期间的每个条目。这是最初的地方
Toolstipster 被创建并分配了一个唯一的 id
eventAfterAllRender:这基本上是在
eventRender,这是我们进行 post 渲染整理的地方,例如
检查无效图像链接等并替换删除任何图像
icons/references 以及更改或 disable/remove 工具提示,如果
不再需要。如果需要,我们在这里替换工具提示内容
否则我们将 Tooltipster 内容设置为传递的任何数据
沿形成 eventRender 过程。
eventMouseover: 本节持有事件悬停方法
负责显示 Tooltipster 工具提示。
我创建了一个示例 fiddle 使用我们代码的简化版本和几个测试事件而不是使用外部数据源,但是当我在我们自己的代码中使用此代码时结果是一样的环境。我包含以下内容以反映我们的环境:Bootstrap 4.4.0 - FullCalendar 3.10.1 - 工具提示 4.2.7
如果将鼠标悬停在其中一个事件上,工具提示只会显示部分指针的值,例如#tt_event1
(它指向的元素的id)而不是内容部分中设置的
例如<div class="tooltip_templates"><span id="tt_event1" class="tooltip_content"><img src="https://hackernoon.com/hn-images/1*cmqZiJz1TuUedRoeI3g_Iw.jpeg" width="450" height="auto"><p style="text-align:left;"><span class="flag-icon flag-icon-uk"></span><strong class="title">Tips for Writing Cleaner Code</strong><br>optional desctiptive text can go here</p></span></div>
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView: 'month',
header: '',
defaultDate: '2020-03-01',
events: [{
id: 'event1',
className: 'UK',
title: 'Tips for Writing Cleaner Code',
description: 'I decided to write an article that will be useful for beginners to understand their mistakes and to put together some practices. /n source: hackernoon.com/tips-for-writing-cleaner-better-code-e36ffeb55526',
start: '2020-03-02', end: '2020-03-02'
}, {
id: 'event2',
className: 'NL',
title: 'Modern Style of Javascript with Arrow Functions',
description: 'The complete explanation of Arrow functions in Javascript, and how it helps developers to write flexible and consistent code. /n source: hackernoon.com/modern-style-of-javascript-with-arrow-functions-lg1x3474',
start: '2020-03-04', end: '2020-03-11'
}
],
eventRender: function(event, element, view) {
window.dataE = window.dataE || [];
element.attr( 'id', event.id );
var /* Vars */
desc = (event.description), url='',
urlEvent1 = "https://hackernoon.com/hn-images/1*cmqZiJz1TuUedRoeI3g_Iw.jpeg",
urlEvent2 = "https://images.unsplash.com/photo-1527427337751-fdca2f128ce5?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEwMDk2Mn0",
tipRef="tip_content_"+event.id, idTip="#"+tipRef
/* End Vars */;
// passing 2 extra image params manually - usually from seperate data routine
if (event.title == 'Tips for Writing Cleaner Code') { url = urlEvent1; } else { url = urlEvent2; }
var tt_ref = '#tt_'+event.id;
var className = ''+event.className;
var cc = className.toLowerCase();
var tt = '<div class="tooltip_templates"><span id="tt_' + event.id + '" class="tooltip_content"><img src="' + url + '" width="450" height="auto"><p style="text-align:left;"><span class="flag-icon flag-icon-' + cc + '"></span><strong class="title">' + event.title + '</strong><br>optional desctiptive text can go here</p></span></div>';
element.attr( 'data-tooltip-content', tt_ref ); // the tooltip pointer
element.attr( 'data-tt-tooltipser' ); element.attr( 'data-tt-tooltipser', tt );
element.attr( 'tt_title' ); element.attr( 'tt_title', event.title );
element.addClass('tt_tooltip tt_group');
var eID = '#'+event.id;
// Tooltipster
if (!element.hasClass('tt_added')) {
$(eID).tooltipster();
$(eID).tooltipster({
//content: tipContent,
//contentCloning: true,
trigger: 'hover',
//multiple: true,
animation: 'fade',
arrow: true,
delay: 300,
maxWidth: 600,
contentAsHTML: true,
debug: true
});
element.addClass('tt_added');
}
// create array of event.IDs for use in [eventAfterAllRender]
if (Array.isArray(dataE)){
var json = JSON.stringify(event.id),
item = dataE.find(el => JSON.stringify(el) === json);
if (typeof item !== 'undefined'){
return false;
} else {
dataE.push(event.id);
}
}
},
eventAfterAllRender: function(event, element){
// get events from dataE array created during [eventRender]
var count = 0;
for (var i=0; i<dataE.length; i++) {
var id = dataE[i], eID = '#'+id ;
// now obtain the tooltip & tooltipster variables for each event
var tipTooltipRef = $(eID).attr('data-tooltip-content');
var tipTooltipsterContent = $(eID).attr('data-tt-tooltipser');
console.log("tipTooltipRef:", tipTooltipRef);
console.log("tipTooltipsterContent:", tipTooltipsterContent);
console.log("$(eID)", $(eID));
// append the tooltipster content aquired via tooltipster var
$(eID).append(tipTooltipsterContent);
$(eID).tooltipster();
// set the content pointer
$(eID).tooltipster('content', tipTooltipRef );
//TOOLTIPSTER: update any necessary Tooltip content
$('.fc-event').mouseenter(function() {
if (tipTooltipRef == '' || tipTooltipRef == 'undefined'){
$(eID).tooltipster('content', 'Invalid image link ');
// OR simply: $(this).tooltipster('disable');
} else {
var t = $(eID).attr('tt_title'), tt_Title = '<div class="ttTitle">'+t+'</div>' ;
var tt_element = $(eID).find('.tooltipster-content');
tt_element.append(tt_Title);
$(eID).tooltipster('option','contentAsHTML','true');
$(eID).tooltipster('content', tipTooltipRef);
if (!$(eID).hasClass('tt_added')) {
$(eID).tooltipster();
$(eID).tooltipster({
content: tipTooltipRef,
//contentCloning: true,
trigger: 'hover',
//multiple: true,
animation: 'fade',
arrow: true,
delay: 300,
maxWidth: 600,
contentAsHTML: true,
//debug: true
});
// bind on start events (triggered on mouseenter)
$(eID).on('start', function(event) {
if ($(event.instance.elementOrigin()).hasClass('tt_group')){
var instances = $.tooltipster.instances('.tt_group'),
open = false,
duration;
$.each(instances, function (i, instance) {
if (instance !== event.instance) {
// if another instance is already open
if (instance.status().open){
open = true;
// get the current animationDuration
duration = instance.option('animationDuration');
// close the tooltip without animation
instance.option('animationDuration', 0);
instance.close();
// restore the animationDuration to its normal value
instance.option('animationDuration', duration);
}
}
});
// if another instance was open
if (open) {
duration = event.instance.option('animationDuration');
// open the tooltip without animation
event.instance.option('animationDuration', 0);
event.instance.open();
// restore the animationDuration to its normal value
event.instance.option('animationDuration', duration);
// now that we have opened the tooltip,
//the hover trigger must be stopped
event.stop();
}
}
});
$(eID).addClass('tt_added');
}
}
});
}
},
eventMouseover: function(view, event, element){
//TOOLTIPSTER: update any necessary Tooltip content
var tipContent = $(this).attr('data-ttipster');
var id = event.id //$(this).attr('id');
var eID = '#'+id;
var tipID = '#tt_'+id;
if (tipContent == '' || tipContent == 'undefined'){
$(eID).tooltipster('content', 'Invalid image detected: unable to display at present ');
$(eID).tooltipster('disable');
// or $(this).tooltipster('destroy');
} else {
// TOOLTIPSTER: Not really req now as tipContent is set @ evenRender
//$(eID).tooltipster('option','contentAsHTML','true');
$(eID).tooltipster('option','multiple','true');
$(eID).tooltipster({
functionInit: function(instance, helper){
var content = $(helper.origin).find(tipID).detach();
instance.content(content);
}
});
if (!$(this).hasClass('tt_added')) {
$(eID).tooltipster();
$(eID).tooltipster({
content: tipContent,
//contentCloning: true,
trigger: 'hover',
//multiple: true,
animation: 'fade',
arrow: true,
delay: 300, //[300, 100]
maxWidth: 600,
contentAsHTML: true,
debug: true
});
// bind on start events (triggered on mouseenter)
$(this).on('start', function(event) {
if ($(event.instance.elementOrigin()).hasClass('tt_group')) {
var instances = $.tooltipster.instances('.tt_group'),
open = false,
duration;
$.each(instances, function (i, instance) {
if (instance !== event.instance) {
// if another instance is already open
if (instance.status().open){
open = true;
// get the current animationDuration
duration = instance.option('animationDuration');
// close the tooltip without animation
instance.option('animationDuration', 0);
instance.close();
// restore the animationDuration to its normal value
instance.option('animationDuration', duration);
}
}
});
// if another instance was open
if (open) {
duration = event.instance.option('animationDuration');
// open the tooltip without animation
event.instance.option('animationDuration', 0);
event.instance.open();
// restore the animationDuration to its normal value
event.instance.option('animationDuration', duration);
// now that we have opened the tooltip,
//the hover trigger must be stopped
event.stop();
}
}
});
$(this).addClass('tt_added');
}
}
},
eventClick: function(event, element, view) {
var e = (event.description);
if (e != null){
var chr = e.length;
// event click coded goes here
alert(e);
}
}
});
});
/* tooltipster.js */
.tooltip_templates {
display: none;
}
.tooltipster-content{
/*display: flex;
flex-direction: column;*/
}
.ttTitle {
}
/*! suit-flex-embed v1.4.0 | MIT License | github.com/suitcss */
.FlexEmbed {
display: block;
overflow: hidden;
position: relative;
}
.FlexEmbed:before {
content: "";
display: block;
width: 100%;
}
.FlexEmbed--16by9:before {
padding-bottom: 56.25%;
}
.FlexEmbed--4by3:before {
padding-bottom: 75%;
}
.FlexEmbed--1by1:before {
padding-bottom: 100%;
}
.CoverImage {
background-position: 50%;
background-repeat: no-repeat;
background-size: cover;
margin: 0 auto 1em;
max-height: 600px;
max-width: 600px;
}
.CoverImageX2 {
background-color: #808080;
background-position: 50%;
background-repeat: no-repeat;
background-size: 100% 100%; /*cover; contain;*/
margin: 0 auto 1em;
max-height: 2400px;
max-width: 1200px;
}
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/css/bootstrap.min.css" integrity="sha384-SI27wrMjH3ZZ89r4o+fGIJtnzkAnFs3E4qz9DIYioCQ5l9Rd/7UAa8DHcaL8jkWt" crossorigin="anonymous" />
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js" integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P" crossorigin="anonymous"></script>
<!-- FulCalendar -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.css" integrity="sha256-tXJP+v7nTXzBaEuLtVup1zxWFRV2jyVemY+Ir6/CTQU=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js" integrity="sha256-4iQZ6BVL4qNKlQ27TExEhBN1HFPvAvAMbFavKKosSWQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.js" integrity="sha256-O04jvi1wzlLxXK6xi8spqNTjX8XuHsEOfaBRbbfUbJI=" crossorigin="anonymous"></script>
<!-- Tooltipster -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.main.min.css" integrity="sha256-xlmCQ8IjIIx7gqrIAb5x5kEU30jJJm0/DEmrjgLow/E=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.bundle.min.css" integrity="sha256-Qc4lCfqZWYaHF5hgEOFrYzSIX9Rrxk0NPHRac+08QeQ=" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.main.min.js" integrity="sha256-9gPC19rdxygnD5cXHFodzczLKeucNZ/dgzLhkKvNtQM=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.bundle.min.js" integrity="sha256-NOU7KrY2aTI4PxDegqYUIknk9qfxVCS0E4JfE9aMwaA=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js" integrity="sha256-b9JNfGq08bjI5FVdN3ZhjWBSRsOyF6ucACQwlvgVEU4=" crossorigin="anonymous"></script>
<div id='calendar'></div>
或者如果你喜欢 fiddle 我之前做的:https://jsfiddle.net/magicmb/3manqpho/
可选:附加奖励问题
我们的 Tooltipster.JS 实施的另一个潜在问题,也许我可能必须为此创建一个单独的 SO 案例,但无论如何我都会在这里快速提及它,因为我正在努力创建一个工作 fiddle 为它。我试图用我的第二个事件做的是展示另一个稍微奇怪的工具提示行为:
Tooltipster seems to have difficulty with events that span several
days and different rows in FullCalendar. They only seem to work
if hovering on the first part of the event i.e. the bit in row1
and not when hovering over the second part in row2.
不幸的是,现在我无法在测试中展示这一点 fiddle。原因是很可能有必要为每一天创建单独的事件(FullCalendar 中的元素),即使我的单个事件似乎由于某种原因进入第一行的末尾但没有进入下一行。因此,我无法像我在测试用例中所做的那样,通过创建一个跨越两周且具有单一开始日期和单一结束日期的事件来显示古怪的行为。
我们自己的系统使用外部数据源,而这从来都不是很明显。此外,它仅适用于我们之前的 Tooltip.js 实现,最初我还认为它适用于 Tooltipster,但现在我不相信了。也许这只是 Tooltipster 更高版本的一个问题 [EDIT:实际上是由于之前使用 data-tooltip-content 来存储整个工具提示]或者它们的工作方式可能与以前的版本略有不同。在任何情况下,如果你们中的任何人对此有所了解,请随时提及它,除了上面的主要问题是让 HTML 工具提示使用指针 inside data-tooltip-content
.
经过相当长一段时间的考虑和尝试各种事情以及参考 Tootltipster 和 FullCalendar 文档以及许多 Github 问题等,我找到了我的问题。
关键步骤:
所有工具提示 都需要作为 FullCalendar [eventRender] 过程的一部分创建,我们 不会通过 [eventMouseover][=65= 触发任何东西].此外,无需将 Tooltipster 工具提示绑定到 event.id
,您需要 将其与 tooltipster class 相关联 或者在我的例子中 .tt_tooltip
class 由于 "tooltip" 名称与 [=89= 冲突].
另一个需要注意的重要事项是 我们似乎只是在 [eventRender] 期间初始化 每个 Tooltipster 工具提示以及设置选项,FullCalendar 文档 指出:
eventRender
Triggered while an event is being rendered. A hook for modifying its DOM.
function( event, element, view ) { }
event is the Event Object that is attempting to be rendered.
element is a newly created jQuery element that will be used for rendering.
额外的重要步骤:
我们需要在 doc.ready[结束时执行 generic Tooltipster 工具提示 initiate =65=] 对于所有 以前创建的工具提示 使用 tt_tooltip class: $('.tt_tooltip').tooltipster();
编辑: 根据我在下面的附加评论,这只是一个示例,在我的测试/实时环境中,我不得不在 [eventAfterAllRender:
] 而不是 doc.ready!
这是更正后的代码示例:
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView: 'month',
header: '',
defaultDate: '2020-03-01',
events: [{
id: 'event1',
className: 'GB',
title: 'Tips for Writing Cleaner Code',
description: 'I decided to write an article that will be useful for beginners to understand their mistakes and to put together some practices. /n source: hackernoon.com/tips-for-writing-cleaner-better-code-e36ffeb55526',
start: '2020-03-02', end: '2020-03-02'
}, {
id: 'event2',
className: 'NL',
title: 'Modern Style of Javascript with Arrow Functions',
description: 'The complete explanation of Arrow functions in Javascript, and how it helps developers to write flexible and consistent code. /n source: hackernoon.com/modern-style-of-javascript-with-arrow-functions-lg1x3474',
start: '2020-03-04', end: '2020-03-11'
}
],
eventRender: function(event, element, view) {
window.dataE = window.dataE || [];
element.attr( 'id', event.id );
var eExist = false;
// create array of event.IDs for use in [eventAfterAllRender]
if (Array.isArray(dataE)){
var json = JSON.stringify(event.id),
item = dataE.find(el => JSON.stringify(el) === json);
if (typeof item !== 'undefined'){
return false;
} else {
dataE.push(event.id);
eExist = true;
}
}
var /* Vars */
desc = (event.description), url='',
urlEvent1 = "https://hackernoon.com/hn-images/1*cmqZiJz1TuUedRoeI3g_Iw.jpeg",
urlEvent2 = "https://images.unsplash.com/photo-1527427337751-fdca2f128ce5?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEwMDk2Mn0",
tipRef="tip_content_"+event.id, idTip="#"+tipRef
/* End Vars */;
// passing 2 extra image params manually - usually from seperate data routine
if (event.title == 'Tips for Writing Cleaner Code') { url = urlEvent1; } else { url = urlEvent2; }
var tt_ref = '#tt_'+event.id;
//var tt_ref = 'tt_'+event.id;
var className = ''+event.className;
var cc = className.toLowerCase();
var tt = '<div class="tooltip_templates"><span id="tt_' + event.id + '" class="tooltip_content"><img src="' + url + '" width="350" height="auto"><p style="text-align:left;"><span class="flag-icon flag-icon-' + cc + '"></span> <span class="tt_title"><strong>' + event.title + '</strong></span><br><span class="tt_desc">optional desctiptive text can go here</span></p></span></div>';
element.attr( 'data-tooltip-content', tt_ref ); // the tooltip ref selector (pointer)
element.attr( 'data-tt-tooltipster' ); element.attr( 'data-tt-tooltipster', tt ); // tootipster content stored for [AfterEventRender]
element.attr( 'tt_title' ); element.attr( 'tt_title', event.title );
element.addClass('tt_tooltip'); // add the tooltipster class (using 'tt_tooltip' due to Bootstrap 'tooltip' conflict )
element.append(tt); // append tooltipster content to each fullCalendar element
// Tooltipster - as per Tooltipster Doc use 'tooltip' class (in our case 'tt_tooltip' due to Bootstrap conflict)
$('.tt_tooltip').tooltipster();
$('.tt_tooltip').tooltipster({
content: tt_ref,
contentCloning: true,
trigger: 'hover',
multiple: true,
animation: 'fade',
arrow: true,
delay: 300,
maxWidth: 600,
contentAsHTML: true,
debug: true
});
},
eventAfterAllRender: function(event, element){
// get events from dataE array created during [eventRender]
var count = 0;
for (var i=0; i<dataE.length; i++) {
var id = dataE[i], eID = '#'+id ;
// obtain the tooltip is selector & tooltipster data each event
var tipTooltipRef = $('.fc-body').find(eID).closest('.tt_tooltip').attr('data-tooltip-content');
var tipTooltipsterContent = $('.fc-body').find(eID).closest('.tt_tooltip').attr('data-tt-tooltipster');
console.log("tipTooltipRef:", tipTooltipRef);
console.log("tipTooltipsterContent:", tipTooltipsterContent);
// update / disable Tooltipster content if necessary
if (tipTooltipRef == '' || tipTooltipRef == 'undefined'){
$('.fc-body').find(eID).closest('.tt_tooltip').tooltipster('content', 'Invalid image link ');
}
}
},
eventMouseover: function(view, event, element){
//Not requried for Tooltipster Tooltips - handled by Tooltipster option (trigger: 'hover')
},
eventClick: function(event, element, view) {
var e = (event.description);
if (e != null){
var chr = e.length;
// event click coded goes here
alert(e);
}
}
//end: fullCalendar
});
// Even though tooltips are created during [eventRender] the generic class gets initiated here.
$('.tt_tooltip').tooltipster(); //{contentCloning: true}
//end: doc.ready
});
/* tooltipster.js */
.tooltip_templates {
display: none;
}
.tooltipster-content{
/*display: flex;
flex-direction: column;*/
}
.ttTitle {
}
/*! suit-flex-embed v1.4.0 | MIT License | github.com/suitcss */
.FlexEmbed {
display: block;
overflow: hidden;
position: relative;
}
.FlexEmbed:before {
content: "";
display: block;
width: 100%;
}
.FlexEmbed--16by9:before {
padding-bottom: 56.25%;
}
.FlexEmbed--4by3:before {
padding-bottom: 75%;
}
.FlexEmbed--1by1:before {
padding-bottom: 100%;
}
.CoverImage {
background-position: 50%;
background-repeat: no-repeat;
background-size: cover;
margin: 0 auto 1em;
max-height: 600px;
max-width: 600px;
}
.CoverImageX2 {
background-color: #808080;
background-position: 50%;
background-repeat: no-repeat;
background-size: 100% 100%; /*cover; contain;*/
margin: 0 auto 1em;
max-height: 2400px;
max-width: 1200px;
}
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/css/bootstrap.min.css" integrity="sha384-SI27wrMjH3ZZ89r4o+fGIJtnzkAnFs3E4qz9DIYioCQ5l9Rd/7UAa8DHcaL8jkWt" crossorigin="anonymous" />
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js" integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P" crossorigin="anonymous"></script>
<!-- FulCalendar -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.css" integrity="sha256-tXJP+v7nTXzBaEuLtVup1zxWFRV2jyVemY+Ir6/CTQU=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js" integrity="sha256-4iQZ6BVL4qNKlQ27TExEhBN1HFPvAvAMbFavKKosSWQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.js" integrity="sha256-O04jvi1wzlLxXK6xi8spqNTjX8XuHsEOfaBRbbfUbJI=" crossorigin="anonymous"></script>
<!-- Tooltipster -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.main.min.css" integrity="sha256-xlmCQ8IjIIx7gqrIAb5x5kEU30jJJm0/DEmrjgLow/E=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.bundle.min.css" integrity="sha256-Qc4lCfqZWYaHF5hgEOFrYzSIX9Rrxk0NPHRac+08QeQ=" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.main.min.js" integrity="sha256-9gPC19rdxygnD5cXHFodzczLKeucNZ/dgzLhkKvNtQM=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.bundle.min.js" integrity="sha256-NOU7KrY2aTI4PxDegqYUIknk9qfxVCS0E4JfE9aMwaA=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js" integrity="sha256-b9JNfGq08bjI5FVdN3ZhjWBSRsOyF6ucACQwlvgVEU4=" crossorigin="anonymous"></script>
<div id='calendar'></div>
THE ISSUE: Using Tooltipster.JS with FullCalendar and / or dynamically rendered content
我已经抓耳挠腮琢磨了好一阵子了,但我离开始时的状态还很远。我已经阅读并重新阅读了 Tooltipster 文档,但我似乎无法使用 'data-tooltip-content'
作为指向 #idTooltipsterElement
的指针。它只是显示 'data-tooltip-content' 的 value,即使文档明确指出:
工具提示文档指出:
5. Use HTML inside your tooltips
Tooltipster allows you to use any HTML markup inside your tooltips. It means that you can insert things like images and text formatting tags.
Instead of a title attribute, use a data-tooltip-content attribute to provide a selector that corresponds to the HTML element of your page that should be used as content. This is your HTML:
<span class="tooltip" data-tooltip-content="#tooltip_content">This span has a tooltip with HTML when you hover over it!</span> <div class="tooltip_templates"> <span id="tooltip_content"> <img src="myimage.png" /> <strong>This is the content of my tooltip!</strong> </span> </div>
In your CSS file, add
.tooltip_templates { display: none; }
so that the content does not get displayed outside of the tooltip.
当我们从 Tippy.js 移动到 Tooltipster.js 时,我遇到了其他问题,谢天谢地,这些问题不存在在 Tootipster 中,但我发现前者更容易设置,尽管它的功能和文档数量远不及后者。
但是:我之前只是使用 'data-tooltip-content'
将我的整个 HTML 元素放入其中,奇怪的是我注意到 这也适用于 Tooltipster 作为好吧,即使他们的文档似乎更喜欢上述方法。现在通常我会简单地保留我已经熟悉的旧方法,但不幸的是,这带来了一整套 与 post 事件渲染更改相关的潜在挑战(我们可能会在我们的页面上广泛使用的 FullCalendar 中实施),以及我已经经历过的 formatting / styling difficulties。
我有理由相信记录在案的 Tooltipster 中描述的 首选解决方案 将是我们实现的理想选择,但我只是无法弄清楚如何让它在我们的环境中工作fullCalendar环境分解为以下程序流程:
eventRender:从事件源中提取事件并渲染 所选月份/期间的每个条目。这是最初的地方 Toolstipster 被创建并分配了一个唯一的 id
eventAfterAllRender:这基本上是在 eventRender,这是我们进行 post 渲染整理的地方,例如 检查无效图像链接等并替换删除任何图像 icons/references 以及更改或 disable/remove 工具提示,如果 不再需要。如果需要,我们在这里替换工具提示内容 否则我们将 Tooltipster 内容设置为传递的任何数据 沿形成 eventRender 过程。
eventMouseover: 本节持有事件悬停方法 负责显示 Tooltipster 工具提示。
我创建了一个示例 fiddle 使用我们代码的简化版本和几个测试事件而不是使用外部数据源,但是当我在我们自己的代码中使用此代码时结果是一样的环境。我包含以下内容以反映我们的环境:Bootstrap 4.4.0 - FullCalendar 3.10.1 - 工具提示 4.2.7
如果将鼠标悬停在其中一个事件上,工具提示只会显示部分指针的值,例如#tt_event1
(它指向的元素的id)而不是内容部分中设置的
例如<div class="tooltip_templates"><span id="tt_event1" class="tooltip_content"><img src="https://hackernoon.com/hn-images/1*cmqZiJz1TuUedRoeI3g_Iw.jpeg" width="450" height="auto"><p style="text-align:left;"><span class="flag-icon flag-icon-uk"></span><strong class="title">Tips for Writing Cleaner Code</strong><br>optional desctiptive text can go here</p></span></div>
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView: 'month',
header: '',
defaultDate: '2020-03-01',
events: [{
id: 'event1',
className: 'UK',
title: 'Tips for Writing Cleaner Code',
description: 'I decided to write an article that will be useful for beginners to understand their mistakes and to put together some practices. /n source: hackernoon.com/tips-for-writing-cleaner-better-code-e36ffeb55526',
start: '2020-03-02', end: '2020-03-02'
}, {
id: 'event2',
className: 'NL',
title: 'Modern Style of Javascript with Arrow Functions',
description: 'The complete explanation of Arrow functions in Javascript, and how it helps developers to write flexible and consistent code. /n source: hackernoon.com/modern-style-of-javascript-with-arrow-functions-lg1x3474',
start: '2020-03-04', end: '2020-03-11'
}
],
eventRender: function(event, element, view) {
window.dataE = window.dataE || [];
element.attr( 'id', event.id );
var /* Vars */
desc = (event.description), url='',
urlEvent1 = "https://hackernoon.com/hn-images/1*cmqZiJz1TuUedRoeI3g_Iw.jpeg",
urlEvent2 = "https://images.unsplash.com/photo-1527427337751-fdca2f128ce5?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEwMDk2Mn0",
tipRef="tip_content_"+event.id, idTip="#"+tipRef
/* End Vars */;
// passing 2 extra image params manually - usually from seperate data routine
if (event.title == 'Tips for Writing Cleaner Code') { url = urlEvent1; } else { url = urlEvent2; }
var tt_ref = '#tt_'+event.id;
var className = ''+event.className;
var cc = className.toLowerCase();
var tt = '<div class="tooltip_templates"><span id="tt_' + event.id + '" class="tooltip_content"><img src="' + url + '" width="450" height="auto"><p style="text-align:left;"><span class="flag-icon flag-icon-' + cc + '"></span><strong class="title">' + event.title + '</strong><br>optional desctiptive text can go here</p></span></div>';
element.attr( 'data-tooltip-content', tt_ref ); // the tooltip pointer
element.attr( 'data-tt-tooltipser' ); element.attr( 'data-tt-tooltipser', tt );
element.attr( 'tt_title' ); element.attr( 'tt_title', event.title );
element.addClass('tt_tooltip tt_group');
var eID = '#'+event.id;
// Tooltipster
if (!element.hasClass('tt_added')) {
$(eID).tooltipster();
$(eID).tooltipster({
//content: tipContent,
//contentCloning: true,
trigger: 'hover',
//multiple: true,
animation: 'fade',
arrow: true,
delay: 300,
maxWidth: 600,
contentAsHTML: true,
debug: true
});
element.addClass('tt_added');
}
// create array of event.IDs for use in [eventAfterAllRender]
if (Array.isArray(dataE)){
var json = JSON.stringify(event.id),
item = dataE.find(el => JSON.stringify(el) === json);
if (typeof item !== 'undefined'){
return false;
} else {
dataE.push(event.id);
}
}
},
eventAfterAllRender: function(event, element){
// get events from dataE array created during [eventRender]
var count = 0;
for (var i=0; i<dataE.length; i++) {
var id = dataE[i], eID = '#'+id ;
// now obtain the tooltip & tooltipster variables for each event
var tipTooltipRef = $(eID).attr('data-tooltip-content');
var tipTooltipsterContent = $(eID).attr('data-tt-tooltipser');
console.log("tipTooltipRef:", tipTooltipRef);
console.log("tipTooltipsterContent:", tipTooltipsterContent);
console.log("$(eID)", $(eID));
// append the tooltipster content aquired via tooltipster var
$(eID).append(tipTooltipsterContent);
$(eID).tooltipster();
// set the content pointer
$(eID).tooltipster('content', tipTooltipRef );
//TOOLTIPSTER: update any necessary Tooltip content
$('.fc-event').mouseenter(function() {
if (tipTooltipRef == '' || tipTooltipRef == 'undefined'){
$(eID).tooltipster('content', 'Invalid image link ');
// OR simply: $(this).tooltipster('disable');
} else {
var t = $(eID).attr('tt_title'), tt_Title = '<div class="ttTitle">'+t+'</div>' ;
var tt_element = $(eID).find('.tooltipster-content');
tt_element.append(tt_Title);
$(eID).tooltipster('option','contentAsHTML','true');
$(eID).tooltipster('content', tipTooltipRef);
if (!$(eID).hasClass('tt_added')) {
$(eID).tooltipster();
$(eID).tooltipster({
content: tipTooltipRef,
//contentCloning: true,
trigger: 'hover',
//multiple: true,
animation: 'fade',
arrow: true,
delay: 300,
maxWidth: 600,
contentAsHTML: true,
//debug: true
});
// bind on start events (triggered on mouseenter)
$(eID).on('start', function(event) {
if ($(event.instance.elementOrigin()).hasClass('tt_group')){
var instances = $.tooltipster.instances('.tt_group'),
open = false,
duration;
$.each(instances, function (i, instance) {
if (instance !== event.instance) {
// if another instance is already open
if (instance.status().open){
open = true;
// get the current animationDuration
duration = instance.option('animationDuration');
// close the tooltip without animation
instance.option('animationDuration', 0);
instance.close();
// restore the animationDuration to its normal value
instance.option('animationDuration', duration);
}
}
});
// if another instance was open
if (open) {
duration = event.instance.option('animationDuration');
// open the tooltip without animation
event.instance.option('animationDuration', 0);
event.instance.open();
// restore the animationDuration to its normal value
event.instance.option('animationDuration', duration);
// now that we have opened the tooltip,
//the hover trigger must be stopped
event.stop();
}
}
});
$(eID).addClass('tt_added');
}
}
});
}
},
eventMouseover: function(view, event, element){
//TOOLTIPSTER: update any necessary Tooltip content
var tipContent = $(this).attr('data-ttipster');
var id = event.id //$(this).attr('id');
var eID = '#'+id;
var tipID = '#tt_'+id;
if (tipContent == '' || tipContent == 'undefined'){
$(eID).tooltipster('content', 'Invalid image detected: unable to display at present ');
$(eID).tooltipster('disable');
// or $(this).tooltipster('destroy');
} else {
// TOOLTIPSTER: Not really req now as tipContent is set @ evenRender
//$(eID).tooltipster('option','contentAsHTML','true');
$(eID).tooltipster('option','multiple','true');
$(eID).tooltipster({
functionInit: function(instance, helper){
var content = $(helper.origin).find(tipID).detach();
instance.content(content);
}
});
if (!$(this).hasClass('tt_added')) {
$(eID).tooltipster();
$(eID).tooltipster({
content: tipContent,
//contentCloning: true,
trigger: 'hover',
//multiple: true,
animation: 'fade',
arrow: true,
delay: 300, //[300, 100]
maxWidth: 600,
contentAsHTML: true,
debug: true
});
// bind on start events (triggered on mouseenter)
$(this).on('start', function(event) {
if ($(event.instance.elementOrigin()).hasClass('tt_group')) {
var instances = $.tooltipster.instances('.tt_group'),
open = false,
duration;
$.each(instances, function (i, instance) {
if (instance !== event.instance) {
// if another instance is already open
if (instance.status().open){
open = true;
// get the current animationDuration
duration = instance.option('animationDuration');
// close the tooltip without animation
instance.option('animationDuration', 0);
instance.close();
// restore the animationDuration to its normal value
instance.option('animationDuration', duration);
}
}
});
// if another instance was open
if (open) {
duration = event.instance.option('animationDuration');
// open the tooltip without animation
event.instance.option('animationDuration', 0);
event.instance.open();
// restore the animationDuration to its normal value
event.instance.option('animationDuration', duration);
// now that we have opened the tooltip,
//the hover trigger must be stopped
event.stop();
}
}
});
$(this).addClass('tt_added');
}
}
},
eventClick: function(event, element, view) {
var e = (event.description);
if (e != null){
var chr = e.length;
// event click coded goes here
alert(e);
}
}
});
});
/* tooltipster.js */
.tooltip_templates {
display: none;
}
.tooltipster-content{
/*display: flex;
flex-direction: column;*/
}
.ttTitle {
}
/*! suit-flex-embed v1.4.0 | MIT License | github.com/suitcss */
.FlexEmbed {
display: block;
overflow: hidden;
position: relative;
}
.FlexEmbed:before {
content: "";
display: block;
width: 100%;
}
.FlexEmbed--16by9:before {
padding-bottom: 56.25%;
}
.FlexEmbed--4by3:before {
padding-bottom: 75%;
}
.FlexEmbed--1by1:before {
padding-bottom: 100%;
}
.CoverImage {
background-position: 50%;
background-repeat: no-repeat;
background-size: cover;
margin: 0 auto 1em;
max-height: 600px;
max-width: 600px;
}
.CoverImageX2 {
background-color: #808080;
background-position: 50%;
background-repeat: no-repeat;
background-size: 100% 100%; /*cover; contain;*/
margin: 0 auto 1em;
max-height: 2400px;
max-width: 1200px;
}
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/css/bootstrap.min.css" integrity="sha384-SI27wrMjH3ZZ89r4o+fGIJtnzkAnFs3E4qz9DIYioCQ5l9Rd/7UAa8DHcaL8jkWt" crossorigin="anonymous" />
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js" integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P" crossorigin="anonymous"></script>
<!-- FulCalendar -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.css" integrity="sha256-tXJP+v7nTXzBaEuLtVup1zxWFRV2jyVemY+Ir6/CTQU=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js" integrity="sha256-4iQZ6BVL4qNKlQ27TExEhBN1HFPvAvAMbFavKKosSWQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.js" integrity="sha256-O04jvi1wzlLxXK6xi8spqNTjX8XuHsEOfaBRbbfUbJI=" crossorigin="anonymous"></script>
<!-- Tooltipster -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.main.min.css" integrity="sha256-xlmCQ8IjIIx7gqrIAb5x5kEU30jJJm0/DEmrjgLow/E=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.bundle.min.css" integrity="sha256-Qc4lCfqZWYaHF5hgEOFrYzSIX9Rrxk0NPHRac+08QeQ=" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.main.min.js" integrity="sha256-9gPC19rdxygnD5cXHFodzczLKeucNZ/dgzLhkKvNtQM=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.bundle.min.js" integrity="sha256-NOU7KrY2aTI4PxDegqYUIknk9qfxVCS0E4JfE9aMwaA=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js" integrity="sha256-b9JNfGq08bjI5FVdN3ZhjWBSRsOyF6ucACQwlvgVEU4=" crossorigin="anonymous"></script>
<div id='calendar'></div>
或者如果你喜欢 fiddle 我之前做的:https://jsfiddle.net/magicmb/3manqpho/
可选:附加奖励问题
我们的 Tooltipster.JS 实施的另一个潜在问题,也许我可能必须为此创建一个单独的 SO 案例,但无论如何我都会在这里快速提及它,因为我正在努力创建一个工作 fiddle 为它。我试图用我的第二个事件做的是展示另一个稍微奇怪的工具提示行为:
Tooltipster seems to have difficulty with events that span several days and different rows in FullCalendar. They only seem to work if hovering on the first part of the event i.e. the bit in row1 and not when hovering over the second part in row2.
不幸的是,现在我无法在测试中展示这一点 fiddle。原因是很可能有必要为每一天创建单独的事件(FullCalendar 中的元素),即使我的单个事件似乎由于某种原因进入第一行的末尾但没有进入下一行。因此,我无法像我在测试用例中所做的那样,通过创建一个跨越两周且具有单一开始日期和单一结束日期的事件来显示古怪的行为。
我们自己的系统使用外部数据源,而这从来都不是很明显。此外,它仅适用于我们之前的 Tooltip.js 实现,最初我还认为它适用于 Tooltipster,但现在我不相信了。也许这只是 Tooltipster 更高版本的一个问题 [EDIT:实际上是由于之前使用 data-tooltip-content 来存储整个工具提示]或者它们的工作方式可能与以前的版本略有不同。在任何情况下,如果你们中的任何人对此有所了解,请随时提及它,除了上面的主要问题是让 HTML 工具提示使用指针 inside data-tooltip-content
.
经过相当长一段时间的考虑和尝试各种事情以及参考 Tootltipster 和 FullCalendar 文档以及许多 Github 问题等,我找到了我的问题。
关键步骤:
所有工具提示 都需要作为 FullCalendar [eventRender] 过程的一部分创建,我们 不会通过 [eventMouseover][=65= 触发任何东西].此外,无需将 Tooltipster 工具提示绑定到 event.id
,您需要 将其与 tooltipster class 相关联 或者在我的例子中 .tt_tooltip
class 由于 "tooltip" 名称与 [=89= 冲突].
另一个需要注意的重要事项是 我们似乎只是在 [eventRender] 期间初始化 每个 Tooltipster 工具提示以及设置选项,FullCalendar 文档 指出:
eventRender
Triggered while an event is being rendered. A hook for modifying its DOM.
function( event, element, view ) { }
event is the Event Object that is attempting to be rendered.
element is a newly created jQuery element that will be used for rendering.
额外的重要步骤:
我们需要在 doc.ready[结束时执行 generic Tooltipster 工具提示 initiate =65=] 对于所有 以前创建的工具提示 使用 tt_tooltip class: $('.tt_tooltip').tooltipster();
编辑: 根据我在下面的附加评论,这只是一个示例,在我的测试/实时环境中,我不得不在 [eventAfterAllRender:
] 而不是 doc.ready!
这是更正后的代码示例:
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView: 'month',
header: '',
defaultDate: '2020-03-01',
events: [{
id: 'event1',
className: 'GB',
title: 'Tips for Writing Cleaner Code',
description: 'I decided to write an article that will be useful for beginners to understand their mistakes and to put together some practices. /n source: hackernoon.com/tips-for-writing-cleaner-better-code-e36ffeb55526',
start: '2020-03-02', end: '2020-03-02'
}, {
id: 'event2',
className: 'NL',
title: 'Modern Style of Javascript with Arrow Functions',
description: 'The complete explanation of Arrow functions in Javascript, and how it helps developers to write flexible and consistent code. /n source: hackernoon.com/modern-style-of-javascript-with-arrow-functions-lg1x3474',
start: '2020-03-04', end: '2020-03-11'
}
],
eventRender: function(event, element, view) {
window.dataE = window.dataE || [];
element.attr( 'id', event.id );
var eExist = false;
// create array of event.IDs for use in [eventAfterAllRender]
if (Array.isArray(dataE)){
var json = JSON.stringify(event.id),
item = dataE.find(el => JSON.stringify(el) === json);
if (typeof item !== 'undefined'){
return false;
} else {
dataE.push(event.id);
eExist = true;
}
}
var /* Vars */
desc = (event.description), url='',
urlEvent1 = "https://hackernoon.com/hn-images/1*cmqZiJz1TuUedRoeI3g_Iw.jpeg",
urlEvent2 = "https://images.unsplash.com/photo-1527427337751-fdca2f128ce5?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEwMDk2Mn0",
tipRef="tip_content_"+event.id, idTip="#"+tipRef
/* End Vars */;
// passing 2 extra image params manually - usually from seperate data routine
if (event.title == 'Tips for Writing Cleaner Code') { url = urlEvent1; } else { url = urlEvent2; }
var tt_ref = '#tt_'+event.id;
//var tt_ref = 'tt_'+event.id;
var className = ''+event.className;
var cc = className.toLowerCase();
var tt = '<div class="tooltip_templates"><span id="tt_' + event.id + '" class="tooltip_content"><img src="' + url + '" width="350" height="auto"><p style="text-align:left;"><span class="flag-icon flag-icon-' + cc + '"></span> <span class="tt_title"><strong>' + event.title + '</strong></span><br><span class="tt_desc">optional desctiptive text can go here</span></p></span></div>';
element.attr( 'data-tooltip-content', tt_ref ); // the tooltip ref selector (pointer)
element.attr( 'data-tt-tooltipster' ); element.attr( 'data-tt-tooltipster', tt ); // tootipster content stored for [AfterEventRender]
element.attr( 'tt_title' ); element.attr( 'tt_title', event.title );
element.addClass('tt_tooltip'); // add the tooltipster class (using 'tt_tooltip' due to Bootstrap 'tooltip' conflict )
element.append(tt); // append tooltipster content to each fullCalendar element
// Tooltipster - as per Tooltipster Doc use 'tooltip' class (in our case 'tt_tooltip' due to Bootstrap conflict)
$('.tt_tooltip').tooltipster();
$('.tt_tooltip').tooltipster({
content: tt_ref,
contentCloning: true,
trigger: 'hover',
multiple: true,
animation: 'fade',
arrow: true,
delay: 300,
maxWidth: 600,
contentAsHTML: true,
debug: true
});
},
eventAfterAllRender: function(event, element){
// get events from dataE array created during [eventRender]
var count = 0;
for (var i=0; i<dataE.length; i++) {
var id = dataE[i], eID = '#'+id ;
// obtain the tooltip is selector & tooltipster data each event
var tipTooltipRef = $('.fc-body').find(eID).closest('.tt_tooltip').attr('data-tooltip-content');
var tipTooltipsterContent = $('.fc-body').find(eID).closest('.tt_tooltip').attr('data-tt-tooltipster');
console.log("tipTooltipRef:", tipTooltipRef);
console.log("tipTooltipsterContent:", tipTooltipsterContent);
// update / disable Tooltipster content if necessary
if (tipTooltipRef == '' || tipTooltipRef == 'undefined'){
$('.fc-body').find(eID).closest('.tt_tooltip').tooltipster('content', 'Invalid image link ');
}
}
},
eventMouseover: function(view, event, element){
//Not requried for Tooltipster Tooltips - handled by Tooltipster option (trigger: 'hover')
},
eventClick: function(event, element, view) {
var e = (event.description);
if (e != null){
var chr = e.length;
// event click coded goes here
alert(e);
}
}
//end: fullCalendar
});
// Even though tooltips are created during [eventRender] the generic class gets initiated here.
$('.tt_tooltip').tooltipster(); //{contentCloning: true}
//end: doc.ready
});
/* tooltipster.js */
.tooltip_templates {
display: none;
}
.tooltipster-content{
/*display: flex;
flex-direction: column;*/
}
.ttTitle {
}
/*! suit-flex-embed v1.4.0 | MIT License | github.com/suitcss */
.FlexEmbed {
display: block;
overflow: hidden;
position: relative;
}
.FlexEmbed:before {
content: "";
display: block;
width: 100%;
}
.FlexEmbed--16by9:before {
padding-bottom: 56.25%;
}
.FlexEmbed--4by3:before {
padding-bottom: 75%;
}
.FlexEmbed--1by1:before {
padding-bottom: 100%;
}
.CoverImage {
background-position: 50%;
background-repeat: no-repeat;
background-size: cover;
margin: 0 auto 1em;
max-height: 600px;
max-width: 600px;
}
.CoverImageX2 {
background-color: #808080;
background-position: 50%;
background-repeat: no-repeat;
background-size: 100% 100%; /*cover; contain;*/
margin: 0 auto 1em;
max-height: 2400px;
max-width: 1200px;
}
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/css/bootstrap.min.css" integrity="sha384-SI27wrMjH3ZZ89r4o+fGIJtnzkAnFs3E4qz9DIYioCQ5l9Rd/7UAa8DHcaL8jkWt" crossorigin="anonymous" />
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js" integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P" crossorigin="anonymous"></script>
<!-- FulCalendar -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.css" integrity="sha256-tXJP+v7nTXzBaEuLtVup1zxWFRV2jyVemY+Ir6/CTQU=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js" integrity="sha256-4iQZ6BVL4qNKlQ27TExEhBN1HFPvAvAMbFavKKosSWQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.1/fullcalendar.min.js" integrity="sha256-O04jvi1wzlLxXK6xi8spqNTjX8XuHsEOfaBRbbfUbJI=" crossorigin="anonymous"></script>
<!-- Tooltipster -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.main.min.css" integrity="sha256-xlmCQ8IjIIx7gqrIAb5x5kEU30jJJm0/DEmrjgLow/E=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/css/tooltipster.bundle.min.css" integrity="sha256-Qc4lCfqZWYaHF5hgEOFrYzSIX9Rrxk0NPHRac+08QeQ=" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.main.min.js" integrity="sha256-9gPC19rdxygnD5cXHFodzczLKeucNZ/dgzLhkKvNtQM=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/tooltipster.bundle.min.js" integrity="sha256-NOU7KrY2aTI4PxDegqYUIknk9qfxVCS0E4JfE9aMwaA=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tooltipster@4.2.7/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js" integrity="sha256-b9JNfGq08bjI5FVdN3ZhjWBSRsOyF6ucACQwlvgVEU4=" crossorigin="anonymous"></script>
<div id='calendar'></div>