Finding Specific Parent Elements

When developing custom functionality within EditLive! you occasionally want to work with a specific element in the tree. For example, you might want to find the element that wraps an uneditable section that the user has clicked within. Other examples might be implementing a properties dialog for a custom block tag you've registered or finding the table tag that the caret is within.

We'll use the uneditable section example in this article, but it's simple to adjust this technique for whatever type of element you need to find.

Set Up

We'll assume you've already got a plugin set up and have access to the ELJBean instance for the editor. If not, take a look at our previous article on creating plugins.

Main Loop

The basic idea is to start from the character element (the leaf element in the tree) and work our way back up until we find an element that matches our criteria. If there isn't a matching element we'll eventually reach the root of the tree and return null.

public Element getUneditableBlock(ELJBean bean) {
  JTextPane pane = bean.getHTMLPane();
  HTMLDocument document = (HTMLDocument) pane.getDocument();
  Element element = document.getCharacterElement(pane.getCaretPosition());
  while (element != null && !isUneditableBlock(element)) {
    element = element.getParentElement();
  }
  return element;
}

Note that the getHTMLPane() method returns a standard JTextPane so all the usual Swing text component methods are available. Iterating up the tree is very similar to iterating up a standard DOM.

Conditional Selection

All that's left is to implement the isUneditableBlock method which provides the conditional logic for selecting the element we're looking for. This is the method that will vary based on exactly what you're looking for. In this case we're looking for an element that has a "contenteditable" attribute that's set to "false".

private boolean isUneditableBlock(Element element) {
  AttributeSet attributes = element.getAttributes();
  return attributes.isDefined("contenteditable") &&
    attributes.getAttribute("contenteditable").equals("false");
}

Note that we check attributes.isDefined("contenteditable") as well as that it's set to false because getAttribute will search through the parent elements as well. Also note that the contenteditable attribute isn't recognized by the parser so it appears as a string - recognized attributes use usually instances of HTML.Attribute or CSS.Attribute, for example HTML.Attribute.WIDTH or CSS.Attribute.COLOR.

An alternate implementation of the method that finds the table element would be:

private boolean isUneditableBlock(Element element) {
  return HTML.Tag.TABLE ==
    element.getAttributes().getAttribute(AttributeSet.NameAttribute);
}

Doing Something Useful

Just to round out our example, here's a mouseClicked implementation that would use these methods to select the entire uneditable section whenever the user clicks within it.

public void mouseClicked(MouseEvent e) {
  Element block = getUneditableBlock(bean);
  if (block != null) {
    bean.raiseEvent(new TextEvent(this, TextEvent.SELECT_NODE_ACTION, block, -1));
  }
}

Note that you have to wait for the LOADING_COMPLETE event to fire before adding the mouse listener, otherwise ELJBean.getHTMLPane will return null. For more information, see the previous article on initializing plugins.

Adrian spends his days working out ways to make life easier for Ephox clients through initiatives like LiveWorks! Previously a senior engineer, he has now moved on to the fancier sounding title of CTO.

One Response to “Finding Specific Parent Elements”

  1. Ephox LiveWorks! » Inserting Content Between Uneditable Sections Says:

    [...] Ephox « Finding Specific Parent Elements [...]

Leave a Reply