Мне кажется, абсолютное большинство - пойдет в Google и напишет что-нибудь типа “jQuery tooltip”.
Когда я лично сталкиваюсь с такими задачами, я всегда первым делом стараюсь вспомнить, где я видел всплывающие подсказки в SharePoint? И на ум сразу же приходит Ribbon!
Причем, подсказки на Ribbon могут выглядеть даже еще красивее:
Возникает естественный вопрос: почему бы не использовать такие же всплывающие подсказки в своих собственных решениях? Это знакомый пользователям интерфейс, который отлично сочетается с общим дизайном портала. Да и вообще, эти подсказки действительно красивые.
Мне удалось это сделать, и получить вот такие всплывающие подсказки на моих собственных страницах:
Ниже я расскажу, как этого можно добиться.
Исследование
Кому не интересны исследования, могут безболезненно пролистать вниз до заголовка “Решение”, взять оттуда готовый код, вставить его в свои решения, и радоваться жизни :)Кто остался, поступят не менее мудро: всё-таки в SharePoint многое зависит от того, насколько хорошо ты умеешь исследовать, и опыт успешных исследований всегда ценен. Как минимум, я такие вещи читаю с удовольствием.
В общем, как вы наверное уже догадались, никакой документации по использованию всплывающих подсказок на MSDN нет. Вполне ожидаемо… Пространство имен CUI, кстати, по версии MSDN содержит единственный класс JsonXmlElement :)
На самом деле, там есть еще куча классов, и среди них – интересующий нас CUI.ToolTip.
Однако, использовать его оказывается не так-то просто.
Конструктор выглядит следующим образом:
CUI.ToolTip = function(root, id, title, description, properties)
Всё понятно с title и description, но что за root и что за properties? Оказывается, в качестве root обязательно нужно передавать специальный “корневой” компонент (CUI.Component, обычно это корневой компонент Ribbon на странице), с кучей каких-то заполненных свойств.
Более того, оказалось, что чтобы отобразить всплывающую подсказку, пришлось бы вызывать явно внутренний метод под названием $CT, что неприятно и опасно: это явно генерированное имя, которое при любом следующем обновлении этого файла вполне может измениться.
Поэтому, поковырявшись минут 20 с попытками создания CUI.ToolTip, я решил попробовать пойти другим путем, и посмотреть, откуда используется CUI.ToolTip.
Оказывается, класс CUI.Control, который является базовым классом для всех контролов на Ribbon’е, содержит метод launchToolTip. Этот метод автоматически создает и отображает всплывающую подсказку, согласно свойствам контрола, а эти свойства передаются в конструктор CUI.Control и весьма прозрачны:
CUI.ControlProperties.prototype = { Command: null, Id: null, TemplateAlias: null, ToolTipDescription: null, ToolTipHelpKeyWord: null, ToolTipImage32by32: null, ToolTipImage32by32Class: null, ToolTipImage32by32Top: null, ToolTipImage32by32Left: null, ToolTipSelectedItemTitle: null, ToolTipShortcutKey: null, ToolTipTitle: null, LabelCss: null}
Однако, конструктор CUI.Control по-прежнему требует передачи ему root-объекта, и вообще весь CUI.Control очень уж заточен под Ribbon. К тому же, это будет означать, что мы не сможем показывать всплывающие подсказки на произвольных html элементах, а необходимость все обертывать в CUI.Control это довольно неудобно. Поэтому я решил не тратить больше времени на исследования и вынужден был признать, что совсем стандартными средствами показать всплывающую подсказку мне не удастся.
С другой стороны, я нашел код который показывает стандартные всплывающие подсказки, и кто запрещает этот код хотя бы скопировать и использовать в собственном классе?..
Решение
Итак, решение заключается в создании простенького класса, который отображает всплывающие подсказки аналогично тому, как это делает стандартный класс CUI.ToolTip. Оригинальный сгенерированный код выглядел, мягко сказать, “не очень”, так что я его, скажем так, написал заново. В итоге получился вот такой класс:
/// <reference name="MicrosoftAjax.js" /> Type.registerNamespace("My.Namespace"); My.Namespace._ToolTipManager = function () { var _divId = "my_tooltip"; var _innerDivId = "my_tooltip_inner";this.attachToolTip = function (element, title, description) { $addHandler(element, 'mouseover', function (e) { showTooltip(element, createTitleAndDescriptionHtml(title, description)); }); $addHandler(element, 'mouseout', hideDiv); } this.attachToolTipRaw = function (element, rawHtml) { $addHandler(element, 'mouseover', function (e) { showTooltip(element, rawHtml); }); $addHandler(element, 'mouseout', hideDiv); } function createTitleAndDescriptionHtml(title, description) { return String.format( '<div class="ms-cui-tooltip-title">{0}</div><div class="ms-cui-tooltip-description">{1}</div>', title, description); } function showTooltip(element, rawHtml) { var tooltipDiv = $get(_divId); if (tooltipDiv == null) tooltipDiv = createTooltip(); $get(_innerDivId).innerHTML = rawHtml; displayTooltipNextToElement(tooltipDiv, element); } function displayTooltipNextToElement(tooltipDiv, element) { tooltipDiv.style.display = ''; var loc = Sys.UI.DomElement.getLocation(element); tooltipDiv.style.left = loc.x + 'px'; tooltipDiv.style.top = loc.y + element.offsetHeight + 2 + 'px'; if (tooltipDiv.curTimeout != null) clearTimeout(tooltipDiv.curTimeout); } function createTooltip() { var mainDiv = document.createElement('span') mainDiv.id = _divId; mainDiv.className = 'ms-cui-tooltip'; mainDiv.style.width = 'auto'; mainDiv.style.position = 'absolute'; var bodyDiv = document.createElement('div'); bodyDiv.className = 'ms-cui-tooltip-body'; bodyDiv.style.width = 'auto'; var innerDiv = document.createElement('div'); innerDiv.id = _innerDivId; innerDiv.className = 'ms-cui-tooltip-glow'; innerDiv.style.width = 'auto'; bodyDiv.appendChild(innerDiv); mainDiv.appendChild(bodyDiv); document.body.appendChild(mainDiv); return mainDiv; } function hideDiv() { $get(_divId).style.display = 'none'; }
} My.Namespace._ToolTipManager.registerClass("My.Namespace"); My.Namespace.ToolTipManager = new My.Namespace._ToolTipManager();
На самом деле класс небольшой, особенно в сравнении с оригиналом. Логика работы, надеюсь, очевидна даже без комментариев. Написано с использованием ASP.Net Ajax, который уже включен в SharePoint и как следствие, ничего подключать для работы этого скрипта не требуется.
Обратите внимание: назначение автоматического растягивания по ширине (*.style.width = 'auto';) сделано не случайно. Если этот код убрать, всплывающая подсказка будет фиксированной ширины (около 200 пикселов).
Пример использования:
DW.DigitalSignature.ToolTipManager.attachToolTip($get('myElement'),'Помощь','Это тестовая всплывающая подсказка!');
Этот вызов приведет к тому, что каждый раз при проведении мышкой над элементом с id=myElement, будет всплывать подсказка.
Если вам необходимо использовать более сложные шаблоны, вы можете использовать метод attachToolTipRaw, куда в качестве параметра можно передать строку с HTML-кодом.
Пример более сложного шаблона:
<div class="ms-cui-tooltip-footer"> <span class="ms-cui-img-16by16 ms-cui-img-cont-float"> <img style="vertical-align: top;" src="/Style Library/pdf.png" alt="" /> </span> <div> {0} </div> </div> <div class="ms-cui-tooltip-clear"> </div> <hr /> <div class="ms-cui-tooltip-description"> {1} </div> <div class="ms-cui-tooltip-clear"> </div> <hr /> <div class="ms-cui-tooltip-footer"> <span class="ms-cui-img-16by16 ms-cui-img-cont-float"> <img style="vertical-align: top;" src="/Style Library/settings.png" alt="" /> </span> <div> {2} </div> </div> </div>
Где {0}, {1} и {2} должны быть заменены некоторым текстом. Замена, напомню, может быть легко произведена с помощью функции String.format :)
Этот шаблон позволяет вывести вот такую всплывающую подсказку:
В общем, надеюсь, этот код сэкономит кому-нибудь немного времени, и позволит сделать ваши решения более красивыми и более “SharePoint’овскими” :)
P.S. Если что-то непонятно или не получается, обязательно спрашивайте в комментариях к статье. Ну и если вдруг еще кто-то не подписан на блог, подписывайтесь, чего ждать-то :)
Не могли бы вы меня послать...
ОтветитьУдалитьГде можно почитать про классы? Скопировал этот класс в отдельный js-файл. Подключил его на странице. В FireBug получил следующую ошибку:
Sys.ArgumentException: Sys.ArgumentException: Значение не является именем регистрируемого типа или именем, которое является зарезервированным словом. Имя параметра: typeName
[Прерывать на этой ошибке]
if (parsedName !== this) throw Error.argument('typeName', Sys.Res.badTypeName);