Inject css stylesheet in GWT RichTextArea head - gwt

is it possible to inject a stylesheet into the head of a GWT RichTextArea
Seems as if i place a style element in the body, some browser e.g. IE7 allows the user to delete the node.

I had the same problem, here's the solution to add in the class constructor:
richTextArea.addInitializeHandler(new InitializeHandler() {
public void onInitialize(InitializeEvent ie) {
document = IFrameElement.as(richTextArea.getElement()).getContentDocument();
StyleElement se = document.createStyleElement();
se.setType("text/css");
se.setInnerHTML("some CSS");
BodyElement body = document.getBody();
body.getParentNode().getChild(0).appendChild(se);
}
});

StlyeInjector can directly insert CSS if you don't want to use a CSS file. It gets put into the head as far as I can tell, but for the whole document.

Yes it is. But you need a library like gwtquery to manipulate the dom, or code some jsni.
I'd rather gquery because of its simplicity and it will work with all browsers.
import static com.google.gwt.query.client.GQuery.*;
// First attach the widget to the DOM
RootPanel.get().add(richTextArea);
// We only can manipulate the head, once the iframe document has been created,
// and this happens after it has been attached.
// Because richtTextArea uses a timeout to initialize we need a delay.
$(richTextArea).delay(1,
lazy()
.contents()
.find("head")
.append("<style> body{background: red} </style>")
.done());
With GWT + JSNI you have to do something like this (not tested in all browsers though):
// First attach the widget to the DOM
RootPanel.get().add(richTextArea);
// We only can manipulate the head, once the iframe document has been created,
// and this happens after it has been attached.
// Using a timer because richtTextArea uses a timeout to initialize.
Timer insertCss = new Timer() {
private native Element getHeadElement(Element iframe) /*-{
return iframe.contentWindow.document.head;
}-*/;
public void run() {
Element head = getHeadElement(richTextArea.getElement());
Element style = DOM.createElement("style");
style.setInnerText("body{background: yellow}");
head.appendChild(style);
}
};
// Schedule the timer
insertCss.schedule(1);

Related

Possible bug with GWT gwtquery .live() method

I'm trying to do the following:
I want to add a specific handler for some links, denoted by a class.
$("a.link_list").live("click", new ListLinkHandler());
I need .live() instead of .bind() because new such links will be generated. (I know jQuery's .live() is deprecated in favor of .on(), but gwt-query doesn't have a .on() yet.)
I defined the handler like this (just as the gwtquery example does):
public class ListLinkHandler extends Function {
#Override
public boolean f(Event e) { [...] }
}
However, the handler method is never called when I click the links.
I can see the event listener in Chrome Dev Tools: http://screencloud.net/v/bV5V. I think it's on the body because it's a .live().
I tried using .bind() and it worked fine. The body event listener changed in a a.link_list and the handler does what it's supposed to do, but (as documented, I didn't test) not for newly created links.
I filed a bug for the .live() method, but maybe I'm doing something wrong.
Also, I have no idea how to do it without gwtquery, GWT doesn't seem to have a method for selecting elements by class, neither to continually add the listener to new elements.
It seems you are doing something wrong, but I need more code to be sure. Could you send the complete onModuleLoad code which demonstrates this wrong behavior?
I have written a quick example using live, and it works either when adding new gwt widgets or dom elements with gquery, in both Chrome and FF
public void onModuleLoad() {
$("a.link_list").live("click", new ListLinkHandler());
// Add a new link via gquery
$("<a class='link_list' href=javascript:alert('href') onClick=alert('onClick')>Click </a>").appendTo(document);
// Add a new link via gwt widgets
Anchor a = new Anchor("click");
a.setStyleName("link_list");
a.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Window.alert("clickHandler");
}
});
RootPanel.get().add(a);
}
public class ListLinkHandler extends Function {
#Override
public boolean f(Event e) {
Window.alert("live");
return true;
}
}

GWT 2.4 customized ListBox doesn't fire Change event

I have added some extra functionality to the standard GWT ListBox by extending it like so:
public class FeatureListBox extends ListBox
{
public FeatureListBox()
{
}
public FeatureListBox(boolean isMultipleSelect)
{
super(isMultipleSelect);
}
public FeatureListBox(Element element)
{
super(element);
}
}
Nothing fancy here. However, the Change event is not firing now, or at least the handler (attached per below) is not getting invoked.
FeatureListBox listBox = new FeatureListBox();
listBox.addChangeHandler(new ChangeHandler()
{
public void onChange(ChangeEvent event)
{
// Do something here...
}
});
Any ideas why?
Either remove the no-argument constructor from FeatureListBox or call super() inside it, otherwise the initialization in the superclasses won't happen, which would probably result in what you're seeing.
The problem was in the way I was using my custom list box. In my application I wrap GWT Widgets around existing DOM elements on the page using the static wrap() methods of their widget classes in which the widgets get marked as attached, making them fire events. I didn't do that with my custom list box class originally, so I ended up implementing a static wrap() method similar to the one of the regular ListBox widget and using it in my code. Everything works like a charm now.

Inserting GWT widget into a div element

I'm using a GWT library (gwt-openlayers) which allows me to create a map popup containing arbitrary HTML, similar to Google Maps. I need this HTML to contain a GWT Button widget.
I'm creating some HTML elements on-the-fly like this:
Element outerDiv = DOM.createDiv();
outerDiv.getStyle().setOverflow(Overflow.HIDDEN);
outerDiv.getStyle().setWidth(100, Unit.PCT);
outerDiv.appendChild(new HTML(mapPOI.getHtmlDetails()).getElement());
Button popupButton = new Button("View Property");
popupButton.getElement().getStyle().setFloat(com.google.gwt.dom.client.Style.Float.RIGHT);
outerDiv.appendChild(popupButton.getElement());
Then I'm getting the source HTML for these elements by calling
String src = outerDiv.toString();
and inserting this html into my map marker. Now my map marker displays the content ok, including the button. However, the button won't respond to any events! From what I can gather, this is because the buttons onAttach() method is never being called.
Is there a better way to do this?
Thanks,
Jon
~~~~EDIT~~~~
I'm now trying a new way of doing this, which seems to be the accepted method looking at other similar posts.
First I'm creating my div:
String divId = "popup-" + ref;
String innerHTML = "<div id=\"" +divId + "\"></div>";
Then I'm adding this to my map popup and displaying it (which adds it to the DOM). After the popup has been displayed, I'm getting the Element as follows and trying to wrap a HTMLPanel around it:
Element element = Document.get().getElementById(divId);
HTMLPanel popupHTML = HTMLPanel.wrap(element);
My div element is successfully retrieved. However, HTMLPanel.wrap(element); doesn't complete. The reason for this is that wrap(..) calls RootPanel.detachOnWindowClose(Widget widget), which includes the following assertions:
assert !widgetsToDetach.contains(widget) : "detachOnUnload() called twice "
+ "for the same widget";
assert !isElementChildOfWidget(widget.getElement()) : "A widget that has "
+ "an existing parent widget may not be added to the detach list";
I put some breakpoints in and it seems that the 2nd assertion is failing!
Does anybody have any idea why this might be the case? Should failing this assertion really result in a complete failure of the method (no return)?
Your first approach is good, you just need to register onClick event for your button like this:
DOM.sinkEvents(popupButton.getElement(), Event.ONCLICK);
DOM.setEventListener(popupButton.getElement(), new EventListener() {
#Override
public void onBrowserEvent(Event event) {
//implement the logic after click
}
});
I have checked this, it works 100%!
You might try something like
RootPanel.get("idOfYourMapMarker").add(popupButton);
See RootPanel.get()
Unfortunately, RootPanels are AbsolutePanels which aren't so nice for layout but could work if you just have a simple button to add. You could also try RootLayoutPanel which will give you a LayoutPanel (also not so nice when you just want things to flow). You might end up creating a container widget that does the layout for you, and adding that to the RootPanel.
SimplePanel is a DIV. Perhaps that can be used instead?
You added the element, but you have to keep the hierarchy of the actual GWT Widgets too.
I don't see a clean way to do this, but you could use something like jQuery to grab the button by and ID and add a click handler back to it that would call the original click handler.
private static native void registerEvents(String buttonId, MyClass instance)/*-{
var $ = $wnd.$;
//check click
$('#'+buttonId).live('click', function(e) {
e.preventDefault();
instance.#com.package.MyClass::handleButtonClick(Lcom/google/gwt/event/dom/client/ClickEvent;)(null);
});
}-*/;
Call this registerEvents() either in your onAttach or constructor.
I once had a similar problem. You can use the gwt-openlayer's MapWidget as follows:
private MapWidget createMapWidget() {
final MapOptions defaultMapOptions = new MapOptions();
defaultMapOptions.setDisplayProjection(DEFAULT_PROJECTION);
defaultMapOptions.setNumZoomLevels(TOTAL_ZOOM_LEVELS);
MapWidget mapWidget = new MapWidget(MAP_WIDGET_WIDTH, MAP_WIDGET_HEIGHT, defaultMapOptions);
map = mapWidget.getMap();
return mapWidget;
}
And then add it to any panel be it vertical or horizontal.
MapWidget mapWgt = createMapWidget();
VerticalPanel mainPanel = new VerticalPanel();
mainPanel.add(mapWgt);
...
... add whatever you want
...
You can finally add the created Panel(containing the MapWidget and the gwt widget) to the PopupPanel. Also, you should now be able to add handlers to the gwt button.

In a GWT HTML widget displaying complex HTML, how do I add an event handler for specific subelements?

I'm optimizing a GWT application that previously used a variety of nested panels to work with DIVs and Spans. I generate the entire table as a single SafeHtml object and then assigning it into a single SafeHtml widget.
I now want to be able to track mouseover/mouseout events at the level of the specific 'cell' spans rather than the entire table, but I'm not sure how to do this.
If I add a handler to the HTML widget itself, I'll get events sourced at various elements.
Since 2.0 there is quite a simple way to do it.
For example if you HTML code is contained in some kind of widget (HTMLPanel or HTML), you can calladdDomHandler(<handler>,<eventtyoe>) on that widget, so you will receive events from inner html.
For example if you have a bunch of anchors inside HTMLPanel and you want to know which one was clicked you can do something like this:
panel.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Element element= event.getNativeEvent().getEventTarget().cast();
if(element.getTagName().equals("A")) {
AnchorElement anchor = element.cast();
Window.alert("Anchor with href " + anchor.getHref() + " was clicked");
}
}
}, ClickEvent.getType());
Since you want to track mouseover/out events you will have to use 2 different dom handlers, find out cell you need when event is fired and then change its state.
The way to approach this is:
Find the element you need with one of the DOM methods, like DOM.getElementById(..) or any other means. View Widget.getElement() etc.
Call DOOM.sinkEvents(element,eventBits) or DOM.sinkBitlessEvent(element,eventName) and pass the required events you want to sink in form of a bitmask, like Event.MOUSEEVENTS or using a named event like click or touchstart if using the second method.
set and EventListener on the element, by calling DOM.setEventListerner(element,eventListener) like so:
DOM.setEventListener( element, new EventListener()
{
#Override
public void onBrowserEvent( Event event )
{
if ("click".event.getType()) {
// ..do stuff..
}
}
} );
Only events you've specified in step 2 will be fired to your EventListener, so you need to only handle those.

Wrapping pre-made elements using GWT?

I've been working with GWT for awhile, I can't find a way to integrate it with a preexisting website which is a real downer. My page content is already generated for me using jsp, like:
<div id='A'></div>
<div id='B'></div>
etc.
there is no way for me to do something like this though:
public void onModuleLoad() {
SimplePanel spA = new SimplePanel(
Document.getElementById("A"));
spA.add(new Label("hello"));
SimplePanel spB = new SimplePanel(
Document.getElementById("B"));
spB.setWidth("200px");
etc ..
}
seems like there's no way to just wrap a pre-existing element. Is this true, or am I missing how to do this? I need be able to wrap a bunch of elements like this, to manipulate them later on. I see TextBox, Button, a few other classes have wrap() methods, however nothing like that exists for elements,
Thanks
There is a way to wrap existing DOM elements, like Label's wrap() method. For example:
Label label = Label.wrap(DOM.getElementById("A"));
label.setText("Foo!");
Other GWT classes can wrap DOM elements too, like Button, and CheckBox using its constructor.
Use HTMLPanel:
class MyPanel extends HTMLPanel {
private SimplePanel a = new SimplePanel();
private SimplePanel b = new SimplePanel();
public MyPanel() {
super("<div id="a"></div><div id="b"></div>);
addAndReplaceElement(a, "a");
addAndReplaceElement(b, "b");
}
}