UiDOM

浏览器提供了一个界面,可以使用 DOM(文档对象模型)来检查和操作屏幕上的元素。传统上,JavaScript 程序员使用 DOM 来编程其逻辑的用户界面部分,并且传统上,他们必须考虑 DOM 在不同浏览器上的实现中的许多差异。

为了让你不必担心(通常)在实现用户界面时跨浏览器支持,GWT 提供了一组 小部件面板 类来封装此功能。但有时你可能需要访问 DOM。例如,如果你想要

  • 在你的用户界面中提供 GWT 不支持的功能
  • 编写新的 Widget 类
  • 访问主机页面中直接定义的 HTML 元素
  • 在低级别处理浏览器事件
  • 对加载到浏览器中的 HTML 文档执行一些过滤或其他处理

GWT 为你提供 DOM 包中的类,用于直接与 DOM 交互。这些类为与 DOM 对象交互提供了静态类型接口,以及一定程度的跨浏览器抽象。

使用 DOM 操作小部件

每个小部件和面板都拥有一个底层的 DOM 元素,你可以使用 getElement() 方法访问。你可以使用 getElement() 方法从 DOM 获取底层元素。

以下示例展示了如何设置样式属性来更改小部件的背景颜色。

private HTML htmlWidget;

// Other code to instantiate the widget...

// Change the description background color.
htmlWidget.getElement().getStyle().setBackgroundColor("#ffee80");

这里,从 Widget 超类派生的 getElement() 方法返回一个 DOM Element 对象,该对象代表 DOM 树结构中的一个节点,并向它添加了一个样式属性。

这是一个使用 DOM 不是绝对必要的示例。一种替代方法是使用 样式表,并将不同的样式类与小部件关联,使用 setStylePrimaryName()setStyleName() 方法。

在 DOM 中查找元素

以下示例展示了如何将 JSNI 方法与 Java 代码结合起来操作 DOM。首先,我们有一个 JSNI 例程,它将检索所有作为锚点标记的子元素。元素对象被分配了唯一的 ID,以便从 Java 方便访问

/**
 * Find all child elements that are anchor tags,
 * assign a unique id to them, and return a list of
 * the unique ids to the caller.
 */
private native void putElementLinkIDsInList(Element elt, ArrayList<String> list) /*-{
  var links = elt.getElementsByTagName("a");

  for (var i = 0; i < links.length; i++ ) {
    var link = links.item(i);
    link.id = ("uid-a-" + i);
    [email protected]::add(Ljava/lang/Object;) (link.id);
  }
}-*/;

一旦你找到一个 DOM 元素,你还能用它做什么呢?这段代码遍历上面方法返回的所有锚点标记,然后重写它们的指向位置

/**
 * Find all anchor tags and if any point outside the site, 
 * redirect them to a "blocked" page.
 */
 private void rewriteLinksIterative() {
   ArrayList<String> links = new ArrayList<String>();
   putElementLinkIDsInList(this.getElement(), links);
   for (int i = 0; i < links.size(); i++) {
     Element elt = Document.get().getElementById(links.get(i));
     rewriteLink(elt, "www.example.com");
   }
 }

/**
 * Block all accesses out of the website that don't match 'sitename'
 * @param element An anchor link element
 * @param sitename name of the website to check.  e.g. "www.example.com"
 */
private void rewriteLink(Element element, sitename) {
  String href = element.getPropertyString("href");
  if (null == href) {
    return;
  }

  // We want to re-write absolute URLs that go outside of this site
  if (href.startsWith("http://") &amp;&amp;
      !href.startsWith("http://"+sitename+"/") {
    element.setPropertyString("href", "http://"+sitename+"/Blocked.html");
  }
}

JSNI 方法在每个元素上设置了一个 ID,我们随后将它用作 Document.getElementById(id) 的参数来获取 Java 中的 Element

使用 DOM 捕获浏览器事件

GWT 包含一个 Event 类,作为对原生 DOM 事件的类型化接口。

此示例展示了如何使用 DOM 方法捕获特定元素的键盘事件,并在 事件 分发之前处理它们

private ArrayList<Element> keyboardEventReceivers = new ArrayList<Element>();

/**
 * Widgets can register their DOM element object if they would like to be a
 * trigger to intercept keyboard events
 */
public void registerForKeyboardEvents(Element e) {
  this.keyboardEventReceivers.add(e);
}

/**
 * Returns true if this is one of the keys we are interested in
 */
public boolean isInterestingKeycode(int keycode) {
  // ...
  return false;
}

/**
 * Setup the event preview class when the module is loaded.
 */
private void setupKeyboardShortcuts() {
  // Define an inner class to handle the event
  Event.addNativePreviewHandler(new NativePreviewHandler() {
    public void onPreviewNativeEvent(NativePreviewEvent preview) {
      NativeEvent event = preview.getNativeEvent();

      Element elt = event.getEventTarget().cast();
      int keycode = event.getKeyCode();
      boolean ctrl = event.getCtrlKey();
      boolean shift = event.getShiftKey();
      boolean alt = event.getAltKey();
      boolean meta = event.getMetaKey();
      if (event.getType().equalsIgnoreCase("keypress") || ctrl || shift
          || alt || meta || keyboardEventReceivers.contains(elt)
          || !isInterestingKeycode(keycode)) {
        // Tell the event handler to continue processing this event.
        return;
      }

      GWT.log("Processing Keycode" + keycode, null);
      handleKeycode(keycode);

      // Tell the event handler that this event has been consumed
      preview.consume();
    }
  });
}

/**
 * Perform the keycode specific processing
 */
private void handleKeycode(int keycode) {
  switch (keycode) {
  // ...
  }
}