Java 将GWT事件挂接到外部iframe中的元素上

Java 将GWT事件挂接到外部iframe中的元素上,java,javascript,dom,gwt,event-handling,Java,Javascript,Dom,Gwt,Event Handling,我正在编写一个GWT应用程序,它涉及在iframe中与外部文档交互。作为概念证明,我尝试将单击处理程序附加到按钮 以下代码在javascript中工作 var iframe = document.getElementById("rawJSIFrame"); var doc = iframe.contentDocument; var body = doc.body; var button = doc.getElementsByTagName("input").namedItem("submit")

我正在编写一个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());
    }


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

提前感谢,


Tin

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


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

进一步研究后,我发现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方面是一条红鲱鱼),都将不胜感激

谢谢


Tin

我认为问题在于,在第一个示例中使用包装时,没有调用GWT方法
onAttach()
。您可以尝试在按钮小部件上使用static
wrap
方法。尽管要使用此功能,
输入
必须为
按钮
类型。或者看看
wrap
方法的实现。以下是使用
wrap
方法时修改的代码:

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

Hilbrand对这个问题的看法是正确的,因为没有调用GWT方法
onAttach()

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

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

在创建
ElementWrapper
之后,调用added
wrapper.onAttach()
。工作起来很有魅力

请看我之前的回答。对原始解决方案稍加修改即可使其正常工作。

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

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);
  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
    }
  });
}
iframelement frm=Document.get().createiframelement();
文档d=frm.getContentDocument();
节点列表输入=d.getElementsByTagName(“输入”);
InputElement目标=null;
对于(int i=0;i

GWT使这项工作变得如此困难和缺乏文档,这总是让我感到困惑

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

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);
        }
    }

酷,谢谢你的回复。我想我会探索JSNI路径,不过我想尽量避免编写自定义javascript。谢谢!我知道必须有更干净的方法:)谢谢,谢谢!这正是我在IE7中让它工作所需要的!
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);
        }
    }