【问题标题】:Hooking a GWT event onto an element in an external iframe将 GWT 事件挂接到外部 iframe 中的元素上
【发布时间】:2010-11-11 13:34:27
【问题描述】:

我正在编写一个 GWT 应用程序,该应用程序涉及与 iframe 中的外部文档进行交互。作为概念证明,我正在尝试将点击处理程序附加到按钮。

以下工作在 javascript 中

var iframe = document.getElementById("rawJSIFrame");
var doc = iframe.contentDocument;
var body = doc.body;
var button = doc.getElementsByTagName("input").namedItem("submit");
button.onclick = function() {
    alert("Clicked!");
};

尝试在 GWT 中做同样的事情,我做了以下事情:

public void addClickHandlerToSubmitButton(String buttonElementName, ClickHandler clickHandler) {
    IFrameElement iframe = IFrameElement.as(frame.getElement());
    Document frameDocument = getIFrameDocument(iframe);
    if (frameDocument != null) {
        Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
        ElementWrapper wrapper = new ElementWrapper(buttonElement);
        HandlerRegistration handlerRegistration = wrapper.addClickHandler(clickHandler);
    }
}

private native Document getIFrameDocument(IFrameElement iframe)/*-{
        return iframe.contentDocument;
}-*/;

以下是ElementWrapper类:

public class ElementWrapper extends Widget implements HasClickHandlers {

    public ElementWrapper(Element theElement) {
        setElement(theElement);
    }

    public HandlerRegistration addClickHandler(ClickHandler handler) {
        return addDomHandler(handler, ClickEvent.getType());
    }


}

查找按钮的代码工作正常,但实际的单击事件处理程序没有被调用。以前有没有人遇到过类似的问题,您是如何解决的?

提前致谢,

【问题讨论】:

    标签: java javascript dom gwt event-handling


    【解决方案1】:

    Hilbrand 关于 GWT 方法 onAttach() 未被调用的问题是正确的。

    我实现了你原来的解决方案,将以下方法添加到ElementWrapper

      public void onAttach() {
        super.onAttach();
      }
    

    并在创建ElementWrapper 之后调用添加wrapper.onAttach()。像魅力一样工作!

    【讨论】:

    • 谢谢!我知道必须有一种更清洁的方式:)
    【解决方案2】:

    我预计问题是当您使用第一个示例中的包装时,不会调用 GWT 方法 onAttach()。您可以尝试在 Button 小部件上使用静态 wrap 方法。尽管要使用它,input 必须是 button 类型。或者看看wrap 方法的实现。下面是使用wrap方法时的修改代码:

    Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
    Button button = Button.wrap(buttonElement);
    HandlerRegistration handlerRegistration = button.addClickHandler(clickHandler);
    

    【讨论】:

      【解决方案3】:

      在进一步研究之后,我发现 iframe 是无关紧要的。同样的行为不适用于主机页面上的普通按钮。

      我基本上通过使用 JSNI 来复制 GWT 的部分事件处理机制来修复它。以下作品:

      Element buttonElement = DOM.getElementById("externalButton");
      new CustomElementWrapper(buttonElement).addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
              Window.alert("GWT hooked into button");
          }
      });
      

      CustomElementWrapper 在哪里:

      public class CustomElementWrapper extends Widget implements HasClickHandlers {
          private ClickEventManager clickEventManager;
      
          public CustomElementWrapper(Element theElement) {
              setElement(theElement);
              clickEventManager = new ClickEventManager(theElement);
          }
      
          public HandlerRegistration addClickHandler(ClickHandler handler) {
              //The 'right' way of doing this would be the code below. However, this doesn't work
              // A bug in GWT?
              //      
              //              return addDomHandler(handler, ClickEvent.getType());
              return clickEventManager.registerClickHandler(handler);
          }
      
      
          void invokeClickHandler() {
              clickEventManager.invokeClickHandler();
          }
      
          public boolean isClickHandlerRegistered() {
              return clickEventManager.isClickHandlerRegistered();
          }
      }
      

      最后,实际工作发生的 ClickEventManager 是:

      public class ClickEventManager {
      private boolean clickHandlerRegistered = false;
      private ClickHandler clickHandler;
      private Element element;
      
      public ClickEventManager(Element element) {
          this.element = element;
      }
      
      public void invokeClickHandler() {
          //This shouldn't really be null but we are bypassing GWT's native event mechanism
          //so we can't create an event
          clickHandler.onClick(null);
      }
      
      public boolean isClickHandlerRegistered() {
          return clickHandlerRegistered;
      }
      
      HandlerRegistration registerClickHandler(ClickHandler handler) {
          clickHandler = handler;
      
          if (!clickHandlerRegistered) {
              registerClickHandlerInJS(element);
              clickHandlerRegistered = true;
          }
          return new HandlerRegistration() {
              public void removeHandler() {
                  //For now, we don't support the removal of handlers
                  throw new UnsupportedOperationException();
              }
          };
      }
      private native void registerClickHandlerInJS(Element element)/*-{
          element.__clickManager = this;
          element.onclick 
              = function() {
                  var cm = this.__clickManager; 
                  cm.@com.talktactics.agent2.client.widgets.ClickEventManager::invokeClickHandler()();
              }
      }-*/;
      }
      

      就我个人而言,我讨厌这种解决方案,因为我似乎在重复 GWT 的事件处理,并且很可能引入了令人讨厌的 javascript 内存泄漏。关于为什么我的第一篇文章不起作用的任何想法(记住 iframe 方面是一个红鲱鱼),将不胜感激。

      谢谢,

      【讨论】:

      • 谢谢,谢谢!这正是我让它在 IE7 中工作所需要的!
      【解决方案4】:

      您可能会发现这很有帮助:

      import com.google.gwt.dom.client.Element;
      import com.google.gwt.event.dom.client.ClickEvent;
      import com.google.gwt.event.dom.client.ClickHandler;
      import com.google.gwt.event.dom.client.HasClickHandlers;
      import com.google.gwt.event.shared.HandlerRegistration;
      import com.google.gwt.user.client.ui.AbsolutePanel;
      
      public class DirectPanel extends AbsolutePanel implements HasClickHandlers {
              public DirectPanel(Element elem) {
                      super(elem.<com.google.gwt.user.client.Element> cast());
                      onAttach();
              }
      
              @Override
              public HandlerRegistration addClickHandler(ClickHandler handler) {
                      return addDomHandler(handler, ClickEvent.getType());
              }
      }
      

      然后您就可以将任意容器制作成小部件容器:

        Element root = Document.get().getElementById("target");
        DirectPanel p = new DirectPanel(root);
        Button register = new Button("Register");
        register.addClickHandler(new ClickHandler() {
          @Override
          public void onClick(ClickEvent event) {
            // ...
          }
        });
        p.add(register);
      

      并将事件绑定到任意元素:

        Element root = Document.get().getElementById("target");
        DirectPanel p = new DirectPanel(root);
        p.addClickHandler(new ClickHandler() {
          @Override
          public void onClick(ClickEvent event) {
            // ...
          }
        });
      

      具体你的情况,试试这个:

      IFrameElement frm = Document.get().createIFrameElement();
      Document d = frm.getContentDocument();
      NodeList<Element> inputs = d.getElementsByTagName("input");
      InputElement target = null;
      for(int i = 0; i < inputs.getLength(); ++i) {
        Element e = inputs.getItem(0);
        if (e.getNodeName().equals("submit")) {
          target = InputElement.as(e);
          break;
        }
      }
      if (target != null) {
        DirectPanel p = new DirectPanel(target);
        p.addClickHandler(new ClickHandler() {
          @Override
          public void onClick(ClickEvent event) {
            // TODO Auto-generated method stub
          }
        });
      }
      

      GWT 让我做这件事变得如此困难且记录不充分,这一直让我感到困惑。

      【讨论】:

        【解决方案5】:

        我建议您不要使用 iframe,而是通过 com.google.gwt.http.client.RequestBuilder 从 GWT 发出 http 请求。像这样:

        private void getHtml(String url) {
                RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
        
                rb.setCallback(new RequestCallback() {
        
                    @Override
                    public void onResponseReceived(Request request, Response response) {
                        HTMLPanel html = new HTMLPanel(response.getText());
        
                        // Now you have a widget with the requested page
                        // thus you may do whatever you want with it.
                    }
        
                    @Override
                    public void onError(Request request, Throwable exception) {
                        Log.error("error " + exception);
                    }
                });
        
                try {
                    rb.send();
                } catch (RequestException e) {
                  Log.error("error " + e);
                }
            }
        

        【讨论】:

          【解决方案6】:

          您可以使用 JSNI 来重用您的 JavaScript 代码。您的 javascript 代码将调用对象上的 gwt 方法,该方法将代表 iframe 中的按钮将其抛出。

          至于为什么 GWT 代码不起作用——我猜那是因为它们在常规浏览器事件之上使用了一些层,这些事件可能不能跨越超过 1 帧。不过,这只是一个猜测。您可以将此作为功能/错误请求提交给 GWT 团队。如果我是对的,您的代码看起来还不错。

          【讨论】:

          • 酷,感谢您的回复。我想我会探索 JSNI 路径,但我想尽可能避免编写自定义 javascript。
          【解决方案7】:

          请看我之前的回答。对您的原始解决方案稍作修改即可使其发挥作用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-05-11
            • 2015-04-05
            • 1970-01-01
            • 2014-10-15
            • 1970-01-01
            • 2012-12-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多