2

我想知道是否可以在 RichTextArea 字段上的 iframe 中添加样式表或样式规则?

我需要对默认样式进行一些 CSS 调整,但我无法通过我的应用程序样式表定位 RichTextArea,因为它是在 iframe 中加载的。

4

5 回答 5

1

Vaadin RichTextArea组件的“问题”不仅在于编辑器字段位于iframe元素内部,而且与所有其他Vaadin组件一样,您还必须记住,当DOM ready回调时您的组件将不可用(例如$(document).ready(function() {}),如果使用 jQuery 或绑定到DOMContentLoaded事件的回调)将执行。

这是因为,如您所知,当 Vaadin 应用程序启动时,您实际上还没有 DOM 中的组件,但是vaadin 引导进程会请求并为您处理 UI 的呈现。这实际上也是 GWT 的工作原理(请参阅GWT 如何为每个浏览器提供正确的 Javascript 代码,例如执行 i18n 和浏览器兼容性?)(毕竟 Vaadin 是基于 GWT 的)。

因此,例如,如果您使用 jQuery,并且在vaadinBootstrap.js脚本加载并执行后的一开始就加载了这样的脚本:

$(function() {
   // this code will execute, but no components are available yet.
   var rTa = $(".v-richtextarea"); // this won't select your Rich text area
   var len = rTa.length // len will be 0 here, as no element matches the previous selector because as stated before, there is not an element with such a class in the DOM yet.
});

执行此代码后,创建 UI 组件和布局的“繁重”过程开始,您的小部件集和/或默认小部件集被加载,然后您设置了漂亮的 UI 并准备好与用户交互。

为了自定义诸如 RichTextArea 之类的现有组件,例如将样式元素添加到其 iframe 元素的主体,您当然可以冒险深入 GWT 并使用 JSNI,就像您在答案中所做的那样,但还有另一种方法可以做在我看来,它更紧凑、更简单,并且不需要使用 JSNI。

您需要做的就是JavaScriptExtension在客户端为您的组件实现一个连接器(您可以扩展 Vaadin 的 RichTextArea),查看这个简单的代码示例:

#!java
package com.package.example;

@JavaScript({"vaadin://js/src/rich_text_area_connector.js"})
public class RichTextAreaExtension extends AbstractJavaScriptExtension {

    @Override
    public void extend(AbstractClientConnector connector) {
        super.extend(connector);
    }

}

这是扩展,然后您需要创建客户端连接器,它基本上是一个带有函数的 JavaScript 文件,该函数的名称基于扩展的包名称,当然还有扩展的类名:

#!javascript
com_package_example_RichTextAreaExtension = function() {
    var connectorParentId = this.getParentId();
    var element = this.getElement(parentId); // this is the rich text area element, which at this point is 

    // If you are using jQuery, then you can just select your element like so:
    var jQueryElement = $(element);
    // and do whatever you would normally do with the element like 
    // when you are inside $(document).ready(function() {});

    // or you can add a style element to the head element inside the iframe, doing something like the following:
    $(element).find("iframe").contents().find('head')
         .append('<link rel="stylesheet" href="./VAADIN/themes/your_theme/style_for_richtextarea_body.css" type="text/css" />');
}

你完成了。如您所见,另一个好处是您不必为不同的浏览器(Mozilla、Chrome、IE)编写不同的代码,您只需使用 jQuery,该库将为您处理兼容性。最后一部分是扩展组件本身。正如我之前所说,您可以扩展 Vaadin 的 RichTextArea:

public class RichTextAreaWithStyleOnBody extends RichTextArea {

    public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
        super(caption);
        if (dataSource != null) 
            setPropertyDataSource(dataSource);
        new RichTextAreaExtension().extend(this);
    }

    public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
        this(caption, dataSource);
    }

    public RichTextAreaWithStyleOnBody(String caption) {
        this(caption, null);
    }

    public RichTextAreaWithStyleOnBody(Property<?> dataSource) {
        this(null, dataSource);
    }

    public RichTextAreaWithStyleOnBody() {
        this(null, null);
    }

}

请注意主构造函数中 JavaScript 扩展的使用。最后,您可以像使用任何其他组件一样在布局中使用它:

// Inside your UI's class
@Override
protected void init(VaadinRequest request) {
        VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        setContent(layout);

        RichTextArea rTa = new RichTextAreaWithStyleOnBody("A rich text area with a styled body");
        rTa.setStyleName("myRichTextArea"); // you can do whatever you'll like on the server side just because your rich text area extends a Vaadin server side component.
        rTa.setSizeFull();

        layout.addComponent(rTa);
}
于 2015-02-09T22:49:18.910 回答
0

据我所知,这是不可能的。我遇到了同样的问题,并根据 RichTextArea 中的 vaadin 代码副本编写了一个小插件。我添加了一些额外的方法来设置字体系列和字体大小。不幸的是我最近没有足够的时间来清理我的代码发布一个新版本的插件。

插件的主要功能是将工具栏与区域分离。您可以在此处找到v7分支中的代码:https ://gitorious.org/richtexttoolbar-vaadin-addon/richtexttoolbar-vaadin-addon

于 2013-05-07T19:15:51.753 回答
0

继续搜索后,我发现了这个讨论,这让我找到了一个解决方案: https ://groups.google.com/forum/#!msg/Google-Web-Toolkit/9kwAJNhnamY/1MVfFFRq8tUJ

我最终包装了 RichTextImpl* 类,从父类克隆了 initElement() 方法,并插入了这些行......

...对于 Mozilla/Safari:

_this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.designMode = 'On';
var doc = _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document;
head=doc.getElementsByTagName('head')[0];
link=document.createElement('link');
link.setAttribute('rel',"stylesheet");
link.setAttribute('href',"/path/to/richtext.css" );
link.setAttribute('type',"text/css");
head.appendChild(link);

...对于 IE:

  var ct = "<html><head><style>@import url('/path/to/richtext.css');</style></head><body CONTENTEDITABLE='true'></body></html>" ;
  doc.write( ct );

...在我的 RichTextArea 字段中加载样式表。

于 2013-05-09T14:27:19.033 回答
0

我想出的最简单的方法是扩展 RichTextArea 组件并使用 JavaScript 设置自定义样式:

public class MyRichTextArea extends RichTextArea {

public MyRichTextArea(String className) {
    setStyleName(className);
    String js = "var iframeContainer = document.getElementsByClassName('" + className + "')[0];" +
                "var iframe = iframeContainer.getElementsByTagName('iframe')[0];" +
                "var iframeBody = iframe.contentDocument.getElementsByTagName('body')[0];" +
                "iframeBody.style.fontFamily='Arial';";
    JavaScript.getCurrent().execute(js);
}

}

用法:

MyRichTextArea field = new MyRichTextArea("fieldClassName");

请注意,所有主要浏览器都应支持iframe.contentDocument,但为了更好地支持,应添加额外的调整 - 请参阅获取 iframe 的文档对象

于 2017-09-18T10:10:25.533 回答
0

嗨伙计们,我不知道这是否仍然重要,但我做了一些不同的事情。

我为我的richtextarea 制作了一个标记函数,对于保存在数据库中的标记,只需将它与我在声明的“styles.css”中已经拥有的样式类一起保存即可。

一步一步是这样的,我正在获取样式表并寻找我的令牌类,然后我在 iframe 标题中添加样式,以便我在 iframe 中的令牌类被样式化。

function styleRichtextareaIframe(id, themeClass, tokenClass, tokenSelectedClass) {
  setTimeout(function() {
    // iframe by id selected
    var $iframe = $("#" + id).find(".gwt-RichTextArea");

    var $head = $iframe.contents().find("head");
    var $body = $iframe.contents().find("body");

    var classNameToken = "." + themeClass + " " + "." + tokenClass;
    var classNameToken_selected = "." + themeClass + " " + "." + tokenSelectedClass;
    var fontClass = "." + themeClass + ".v-app, " + "." + themeClass + " " + ".v-window";

    var styleSheets = window.document.styleSheets;
    var styleSheetsLength = styleSheets.length;

    var style = document.createElement('style');
    style.type = 'text/css';
    for (var i = 0; i < styleSheetsLength; i++) {
      var classes = styleSheets[i].rules || styleSheets[i].cssRules;
      for (var x = 0; x < classes.length; x++) {
        if (classes[x].selectorText == classNameToken) {
          style.appendChild(document.createTextNode(getClassFor(classes[x])));
        }
        if (classes[x].selectorText == classNameToken_selected) {
          style.appendChild(document.createTextNode(getClassFor(classes[x])));
        }
        if (classes[x].selectorText == fontClass) {
          style.appendChild(document.createTextNode(getClassFor(classes[x])));
        }

      }
    }

    function getClassFor(classObj) {
      if (classObj.cssText) {
        return classObj.cssText;
      } else {
        return classObj.style.cssText;
      }
    }

    //Adding the classes also to the body
    //of dourse you can change the output string to just give you the style class that you need.
    $body.addClass("v-app " + themeClass);
    $head.append(style);
  }, 200);
}

我不得不插入超时功能,因为 Vaadin RichTextArea 组件的“问题”不仅在于编辑器字段位于 iframe 元素内,而且与所有其他 Vaadin 组件一样,您还必须牢记当 DOM 准备好时,您的组件将不可用(组件不在 DOM 中)。

希望这对某人有所帮助,并为我的英语不好感到抱歉。

干杯。

于 2015-09-24T14:05:15.700 回答