问题:将Tooltipster.JS与FullCalendar和/或动态渲染的内容一起使用
我已经挠了一下头,为此咀嚼了好一阵子,而且我离开始时的位置还很近。我已经阅读并重新阅读了Tooltipster文档,但我似乎根本无法使用'data-tooltip-content'
作为指向#idTooltipsterElement
的指针。即使文档明确指出,它也仅显示“ data-tooltip-content”的value:
工具提示文档状态:
移到Tooltipster.js时,我遇到了其他问题,幸好Tootipster中没有这些问题,但是我发现前者更容易设置,即使它无处不在功能和文档数量都差不多。5。在工具提示中使用HTML]]
工具提示允许您在工具提示中使用任何HTML标记。它表示您可以插入图像和文本格式标签之类的内容。
而不是标题属性,而是使用data-tooltip-content属性提供与您页面的HTML元素相对应的选择器应该用作内容。这是您的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>
在您的CSS文件中,添加
.tooltip_templates { display: none; }
,以便该内容不会在工具提示之外显示。当我们从Tippy.js
但是:我以前只是使用'data-tooltip-content'
将整个HTML元素放入其中,而且奇怪的是,我注意到这也适用于Tooltipster,即使他们的文档似乎更喜欢上述方法。现在,通常我只是保留我已经熟悉的旧方法,但是不幸的是,这带来了与FullCalendar中事件后渲染更改(我们很可能实现)有关的大量潜在挑战
我可以肯定地说,记录在工具提示上的首选解决方案
将是我们实现的理想选择,但是我根本无法弄清楚如何使其在已损坏的fullCalendar环境中工作在以下程序流中:eventRender:从事件源中提取事件并进行渲染所选月份/期间的每个条目。这是最初的地方Toolstipster被创建并分配了唯一的ID
eventAfterAllRender
:这基本上在eventRender,这是我们进行后期渲染整理的地方,例如检查无效的图像链接等,并替换删除任何图像图标/参考,以及在以下情况下更改或禁用/删除工具提示不再需要。如果需要,我们在此处替换工具提示内容否则,我们会将Tooltipster内容设置为传递的任何数据以及eventRender流程。eventMouseover
:此部分包含事件悬停方法负责显示工具提示工具提示。我使用代码的简化版本和几个测试事件而不是使用外部数据源创建了一个样本提琴,但是当我在自己的环境中使用此代码时,结果是相同的。我包括以下内容以反映我们的环境:Bootstrap 4.4.0-FullCalendar 3.10.1
如果将鼠标悬停在其中一个事件上,则工具提示只会显示节指针的值,例如#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/[email protected]/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/[email protected]/dist/css/tooltipster.main.min.css" integrity="sha256-xlmCQ8IjIIx7gqrIAb5x5kEU30jJJm0/DEmrjgLow/E=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/tooltipster.bundle.min.css" integrity="sha256-Qc4lCfqZWYaHF5hgEOFrYzSIX9Rrxk0NPHRac+08QeQ=" crossorigin="anonymous" /> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/tooltipster.main.min.js" integrity="sha256-9gPC19rdxygnD5cXHFodzczLKeucNZ/dgzLhkKvNtQM=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/tooltipster.bundle.min.js" integrity="sha256-NOU7KrY2aTI4PxDegqYUIknk9qfxVCS0E4JfE9aMwaA=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js" integrity="sha256-b9JNfGq08bjI5FVdN3ZhjWBSRsOyF6ucACQwlvgVEU4=" crossorigin="anonymous"></script> <div id='calendar'></div>
或者,如果您喜欢我之前制作的小提琴:https://jsfiddle.net/magicmb/3manqpho/
可选:额外奖金问题
我们的Tooltipster实现的另一个潜在问题,也许我可能不得不为此创建一个单独的SO案例,但是无论如何我都会在这里快速提及,因为我正在努力为其创建一个可行的提琴,以防有人指出我朝着正确的方向前进。我第二次尝试做的是至少在最新的CDN版本的Tootlipster中展示与Tooltipster IMO有关的另一种奇怪行为。我说这是因为我们最初使用的是在其他CDN提供商中偶然发现的旧版本,而我记得这似乎并没有遇到这个问题。无论如何,我会在这里提到它,以防万一回答以上帖子的人也可以为此指出正确的方向。在fullCalendar中,
Tooltipster似乎很难处理几天的事件
和不同行。只有在事件的第一部分悬停时,它们才似乎起作用。将鼠标悬停在row1中的位上,而不是将鼠标悬停在row2中的第二部分上时。现在很遗憾,我无法在测试小提琴上显示此内容。原因是,即使我的单个事件出于某种原因似乎排在第一行的末尾,但可能不是每天都创建一个单独的事件,而不是下一个事件,而不是仅创建一个包含像我在测试用例中所做的那样,一次开始和一次结束日期。
我们自己的系统使用外部数据源,这从未发现过。它也可以与我们以前的Tooltip.js实现一起使用,最初我还认为它可以与Tooltipster一起使用,但是我不敢相信。也许这只是Tooltipster更高版本的问题,或者它们的工作方式与以前的版本略有不同。我不确定。如果您还对此有所了解,除了上面提到的主要问题之外,还可以随时提及[data-tooltip-content
中的HTML工具提示使用指针工作
问题:将Tooltipster.JS与FullCalendar和/或动态呈现的内容一起使用,我已经挠了挠头,仔细咀嚼了好一阵子了,而且我离原地还很近...
经过一段时间的思考和尝试各种事物,并参考了Tootltipster和FullCalendar Docs以及大量的Github问题,然后我弄清楚了我的问题。
关键步骤: