4

When clicking on bold text in the RTE (rich text editor), magically the ribbon button for bold gets activated, as you can see here.

toolbar with bold icon toggled on

The same happens for the other format option buttons you can see on the screenshot. I want to attach to this same functionality, but I don't know how to do this. Somehow the SP Javascript must check the text the mouse just clicked, check its current style (bold) and send this information to the ribbon which in turn activates the bold button so you can deactivate (toggle) it.

There is a very good example of adding a custom markup button (in this case adding a <span class="highlight"></span> around the currently selected text) to the ribbon. Unfortunately this only adds the markup. Of course I could edit the code to check [pseudocode] if exists <span class="highlight"> --> remove so I would have a toggle button for the highlight function - what I don't have is that the button is highlighted once someone selects text with class="highlight".

What I figured out so far: When you click on a bold text or e.g. a link within a rich text editor in SharePoint, a mousedown Javascript event is fired from MicrosoftAjax.js. This gets passed on to SP.UI.RTE.js via Callbacks. I also found RTE.Cursor (or RTE.Canvas.onMouseDown in particular) in some tutorials being used, so maybe I can get notified somehow that the cursor currently resides on class="highlight"?

One thing I could do is to write a custom OnMouseDown event handler and do the same thing SP does - but maybe there is some way I could attach to the same event Microsoft is currently attached to?

Some resources:

1 Answer 1

7

You have two options here. One is to create simple toggle button with this markup:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="8DCC5B17-4B9E-41A7-BA51-65CBC6F8205D"
                Location="CommandUI.Ribbon"
                Sequence="10">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.EditingTools.CPEditTab.Font.Controls._children">
          <ToggleButton
            Id="Ribbon.EditingTools.CPEditTab.Font.CustomRTEToggleButton"
            Alt="My Custom Action"
            Command="CustomRTEToggleButton.Action"
            QueryCommand="CustomRTEToggleButton.Query"
            Sequence="55"
            TemplateAlias="o1"
            Image16by16="/_layouts/1033/images/formatmap16x16.png" Image16by16Top="-112" Image16by16Left="-16"
            ToolTipTitle="My Custom Action" 
            ToolTipDescription="My Custom Action."/>
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler
          EnabledScript="return true;"
          Command="CustomRTEToggleButton.Action"
          CommandAction="javascript:alert('hello there');" />
        <CommandUIHandler
          EnabledScript="return true;"
          Command="CustomRTEToggleButton.Query"
          CommandAction="javascript:alert('query');" />
      </CommandUIHandlers>
    </CommandUIExtension>
  </CustomAction>
</Elements>

alert with "query" will be shown every time when you click (or select text) in rte text editor (this event will be also fire when tab with button will be loaded). So, you can create js function (instead of alert) that will be analyze cursor position somehow (or selected text) and perform required actions. The only trouble is to find correct way to toggle display mode for button. Example of javascript for manipulating with selected text:

var selectedText = RTE.Cursor.get_range().get_text(); - returns selected text
var parentEl = RTE.Cursor.get_range().parentElement() - returns DOM element, which contains current cursor.

Another option is to create custom page component (here and here good explanation with examples). Using second approach your definition should look like this one:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="8DCC5B17-4B9E-41A7-BA51-65CBC6F8205D"
                Location="CommandUI.Ribbon"
                Sequence="10">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.EditingTools.CPEditTab.Font.Controls._children">
          <ToggleButton
            Id="Ribbon.EditingTools.CPEditTab.Font.CustomRTEToggleButton"
            Alt="My Custom Action"
            Command="CustomRTEToggleButton.Action"
            QueryCommand="CustomRTEToggleButton.Query"
            Sequence="55"
            TemplateAlias="o1"
            Image16by16="/_layouts/1033/images/formatmap16x16.png" Image16by16Top="-112" Image16by16Left="-16"
            ToolTipTitle="My Custom Action" 
            ToolTipDescription="My Custom Action."/>
        </CommandUIDefinition>
      </CommandUIDefinitions>
    </CommandUIExtension>
  </CustomAction>
</Elements>

and code in you handleCommand in page component something like this:

handleCommand: function (commandId, properties, sequence) {
        if (commandId === 'CustomRTEToggleButton.Query') {
            var selectedText = RTE.Cursor.get_range().get_text();
            if (/SP/gi.test(selectedText)) {
                properties.On = true;
            }
            else {
                properties.On = false;
            }
        }
    },

properties.On controls if toggle button active or not.
Second approach is a little bit complected, but this is more sharepoint-like style plus you can automatically switch your toggle button using properties.On.

NOTE: QueryCommand attribute available only for several ribbon controls.

UPDATE:
I've created sample solution using second approach, it adds toggle button to the ribbon, the button become active, when we select word sharepoint in the rich text area. Currently it can be downloaded from here. The only problem - I can't figured out how to make button smaller, I've tried different TemplateAlias but with no luck, if you find correct option to make it smaller (as other buttons like bold, italic, etc) please comment. Currently it looks like on the screenshot: enter image description here

7
  • 1
    +1 cause that is an impressive answer! Can you clarify when you'd use option one vs option two? Is it just less complicated?
    – Kit Menke
    Apr 17, 2012 at 19:49
  • As Andrew Connell mentioned, you should use page component because the page component is an actual client object, you have all the power of an object such as private methods and fields if you need to store state, so if you have complicated logic in your handler, you should use page component. In this question, if we are using first option, I really don't know how to switch on (or off) toggle button (may be somehow using js ribbon api, get button instance by id), but if you are using second option, you can toggle button using properties object. Apr 18, 2012 at 7:55
  • Good answer! However I couldn't reproduce your first example. I got the button to load correctly but when clicking it no alert appears. I don't see how the two examples know at all times what the cursor currently resides on. I don't want to click a button and then it changes its toggle state according to the currently selected text - I want this to be live on cursor click within the RTE.
    – Dennis G
    Apr 19, 2012 at 13:13
  • 1
    I've created test solution, see my update. Regarding the first option it works for me. You don't have to click toggle button to change it state, the state changes automatically depending on user actions in the rich text area. Sorry for late reply. Apr 25, 2012 at 7:37
  • amazing answer. Apr 25, 2012 at 11:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.