如何在ckeditor5的`a`标签中添加“ target”属性?

时间:2018-07-12 10:58:24

标签: ckeditor5

我已经为链接创建了自己的插件。现在,我想向插件生成的a标签添加其他一些属性,例如targetrel

但是我无法完成它。这是转换器的我的插件代码。 我应该添加哪些转换器,以便a标签可以支持其他属性?

 
/**
 * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md.
 */

/**
 * @module link/linkediting
 */

import LinkEditing from '@ckeditor/ckeditor5-link/src/linkediting';
import {
    downcastAttributeToElement
} from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';
import { upcastElementToAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
import LinkCommand from './uclinkcommand';
import UnlinkCommand from './ucunlinkcommand';
import { createLinkElement } from '@ckeditor/ckeditor5-link/src/utils';
import { ensureSafeUrl } from './utils';
import bindTwoStepCaretToAttribute from '@ckeditor/ckeditor5-engine/src/utils/bindtwostepcarettoattribute';

/**
 * The link engine feature.
 *
 * It introduces the `linkHref="url"` attribute in the model which renders to the view as a `<a href="url">` element.
 *
 * @extends module:core/plugin~Plugin
 */
export default class UcLinkEditing extends LinkEditing {
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;

        // Allow link attribute on all inline nodes.
        editor.model.schema.extend( '$text', { allowAttributes: 'linkHref' } );

        editor.conversion.for( 'dataDowncast' )
            .add( downcastAttributeToElement( { model: 'linkHref', view: createLinkElement } ) );

        editor.conversion.for( 'editingDowncast' )
            .add( downcastAttributeToElement( { model: 'linkHref', view: ( href, writer ) => {
                return createLinkElement( ensureSafeUrl( href ), writer );
            } } ) );

        editor.conversion.for( 'upcast' )
            .add( upcastElementToAttribute( {
                view: {
                    name: 'a',
                    attribute: {
                        href: true
                    }
                },
                model: {
                    key: 'linkHref',
                    value: viewElement => viewElement.getAttribute( 'href' )
                }
            } ) );

        // Create linking commands.
        editor.commands.add( 'ucLink', new LinkCommand( editor ) );
        editor.commands.add( 'ucUnlink', new UnlinkCommand( editor ) );

        // Enable two-step caret movement for `linkHref` attribute.
        bindTwoStepCaretToAttribute( editor.editing.view, editor.model, this, 'linkHref' );

        // Setup highlight over selected link.
        this._setupLinkHighlight();
    }
}

1 个答案:

答案 0 :(得分:11)

简介

在获得代码之前,我想先解释一下CKEditor 5对内联元素(例如<a>)的使用方法,以便使解决方案更容易理解。有了这些知识,将来不会出现类似的问题。以下内容旨在作为一个全面的教程,因此请仔细阅读。

即使您可能了解理论部分的大部分内容,我还是建议您阅读它以全面了解CKEditor 5中的工作原理。

此外,请注意,我将为原始CKEditor 5插件提供一个解决方案,因为它对于寻求该问题的教程的其他社区成员来说更有价值。尽管如此,我希望借助本教程的见识,您能够将代码示例调整为自定义插件。

此外,请记住,本教程不讨论此插件的UI部分,而只是讨论应如何配置事物以进行转换。添加和删​​除属性是UI或代码其他部分的工作。在这里,我仅讨论引擎内容。

CKEditor 5中的内联元素

首先,让我们确定哪些元素是内联的。通过内联元素,我可以理解诸如<strong><a><span>之类的元素。与<p><blockquote><div>不同,内联元素不构成数据。相反,它们以特定的(视觉和语义)方式标记某些文本。因此,从某种意义上说,这些元素是文本给定部分的特征。结果,我们说文本的给定部分为粗体,或者文本的给定部分为/具有链接。

类似地,在模型中,我们不直接将<a><strong>表示为元素。相反,我们允许将属性添加到文本的一部分。这就是文本特征(粗体,斜体或链接)的表示方式。

例如,在模型中,我们可能有一个带有<paragraph>文本的Foo bar元素,其中bar的{​​{1}}属性设置为bold。我们会这样记录:true。看到那里没有<paragraph>Foo <$text bold="true">bar</$text></paragraph>或任何其他附加元素。只是一些带有属性的文本。稍后,<strong>属性将转换为bold元素。

顺便说一句:来自模型属性的视图元素具有自己的类:view.AttributeElement,并且代替内联元素也可以称为属性元素。可悲的是,名称与作为视图元素属性的“属性”冲突(更糟糕的是,允许属性元素具有属性)。

当然,文本可能具有多个属性,并且所有属性都将转换为它们各自的视图内联元素。请记住,在模型中,属性没有任何设置顺序。这与视图或HTML相反,在视图或HTML中,内联元素彼此嵌套。嵌套发生在从模型到视图的转换期间。这使在模型中的工作变得更加简单,因为要素不需要照顾到破坏或重新布置模型中的元素的情况。

考虑以下模型字符串:

<strong>

这是粗体的<paragraph> <$text bold="true">Foo </$text> <$text bold="true" linkHref="bar.html">bar</$text> <$text bold="true"> baz</$text> </paragraph> 文本,在Foo bar baz上具有链接。在转换过程中,它将转换为:

bar

请注意,<p> <strong>Foo </strong><a href="bar.html"><strong>bar</strong></a><strong> baz</strong> </p> 元素的转换方式始终是最顶层的元素。这是有意的,因此任何元素都不会破坏<a>元素。看到以下错误的视图/ HTML字符串:

<a>

生成的view / HTML有两个彼此相邻的链接元素,这是错误的。

我们使用<p> <a href="bar.html">Foo </a><strong><a href="bar.html">bar</a></strong> </p> 的{​​{3}}属性来定义哪个元素应该位于其他元素之上。大多数元素,例如view.AttributeElement都不关心它,并保留默认优先级。但是,<strong>元素已更改优先级,以保证视图/ HTML中的正确顺序。

复杂的内联元素和合并

到目前为止,我们主要讨论了更简单的内联元素,即没有属性的元素。示例为<a><strong>。相反,<em>具有其他属性。

很容易想到需要对文本的一部分进行标记/样式设置但足够自定义的功能,以至于仅使用标签是不够的。一个示例是字体系列功能。在使用时,它将<a>属性添加到文本中,随后使用适当的fontFamily属性将其转换为<span>元素。

这时,您需要问如果在文本的同一部分上设置了多个这样的属性会发生什么情况?以这个模型为例:

style

以上属性转换如下:

  • <paragraph> <$text fontFamily="Tahoma" fontSize="big">Foo</$text> </paragraph> 转换为fontFamily="value"
  • <span style="font-family: value;">转换为fontSize="value"

那么,我们可以期望什么样的视图/ HTML?

<span class="text-value">

但是,这似乎是错误的。为什么不只有一个<p> <span style="font-family: Tahoma;"> <span class="text-big">Foo</span> </span> </p> 元素?这样会更好吗?

<span>

为解决此类情况,实际上,我们在CKEditor 5转换机制中引入了一种合并机制。

在上述情况下,我们有两个属性可以转换为<p> <span style="font-family: Tahoma;" class="text-big">Foo</span> </p> 。当第一个属性(例如<span>被转换时,视图中还没有fontFamily。因此,<span>被添加了<span>属性。但是,当{ {1}}被转换,视图中已经有stylefontSize识别出这一点并检查这些元素是否可以合并。规则是三个:

  • 元素必须具有相同的<span>
  • 元素必须具有相同的view.Writer
  • 这两个元素均未设置view.Element#name

我们尚未讨论view.AttributeElement#priority属性,但出于简单原因,我现在不再讨论。可以说,对于某些属性元素而言,防止它们合并很重要。

向链接添加另一个属性

这时,应该很清楚如何向view.AttributeElement#id元素添加另一个属性。

所有需要做的就是定义一个新的模型属性(id<a>),并将其转换为具有所需的linkTarget或{ {1}})属性。然后,它将与原始linkRel元素合并。

请记住,原始CKEditor 5链接插件中的<a>元素已指定了自定义target="..."。这意味着由新插件生成的元素必须具有指定的相同优先级才能正确合并。

向上投射合并的属性元素

目前,我们仅讨论了向下转换(即从模型转换为视图)。现在让我们谈谈转换(即从视图转换为模型)。幸运的是,它比上一部分要容易。

有两个可以“转换”的东西-元素和属性。这里没有魔术-元素是元素(rel="..."<a href="..."><a>等),属性是属性(priority<p>等)。

可以将元素上移到元素(<a>-> <strong>)或属性(class=""-> href=""<p>-> {{1} }。属性可以转换为属性。

我们的示例显然需要将元素从元素转换为属性。实际上,<paragraph>元素已转换为<strong>属性,并且bold属性值取自<a>元素的linkHref属性。

自然,人们会为其新的<a>linkHref属性定义相同的转化。但是,这里有一个陷阱。视图的每个部分只能转换一次(“消耗”)一次(向下转换时模型也是如此)。

是什么意思?简而言之,如果已经使用给定的元素名称或给定的element属性转换了一个功能,则这两个功能都无法对其进行转换。这样,功能可以正确地相互覆盖。这也意味着可以引入通用转换器(例如,如果没有其他功能将linkHref识别为可以由该功能转换的功能,则href=""可以转换为<a>。这也有助于发现有冲突的转换器。

回到我们的示例。我们不能定义两个元素到属性转换器来转换同一元素(linkTarget)并期望它们同时工作。一个会覆盖另一个。

由于我们不想更改原始链接插件,因此需要将该转换器保持原样。但是,新插件的转换转换器将是属性到属性转换器。由于该转换器不会转换元素(或元素名称),因此它将与原始转换器一起使用。

代码示例

这是链接目标插件的代码示例。下面我将解释它的某些部分。

linkRel

对于这么长的教程,它肯定是一小段。希望其中大多数是不言自明的。

首先,我们通过定义文本上允许的新属性<div>来扩展priority

然后,我们定义向下转换。使用Schema是因为我们要创建将与原始<paragraph>元素合并的<div>元素。请记住,此处创建的<a>元素的优先级定义为import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import { downcastAttributeToElement } from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters'; import { upcastAttributeToAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters'; class LinkTarget extends Plugin { init() { const editor = this.editor; editor.model.schema.extend( '$text', { allowAttributes: 'linkTarget' } ); editor.conversion.for( 'downcast' ).add( downcastAttributeToElement( { model: 'linkTarget', view: ( attributeValue, writer ) => { return writer.createAttributeElement( 'a', { target: attributeValue }, { priority: 5 } ); }, converterPriority: 'low' } ) ); editor.conversion.for( 'upcast' ).add( upcastAttributeToAttribute( { view: { name: 'a', key: 'target' }, model: 'linkTarget', converterPriority: 'low' } ) ); } } ,就像在原始链接插件中一样。

最后一步是转换转换。如前所述,使用了downcastAttributeToElement帮助程序。在linkTarget配置中,指定仅<a target="...">元素的<a>属性应被转换(<a>)。这并不意味着5元素将被转换!这只是转换器的过滤配置,因此不会转换其他元素的view属性。

最后,两个转换器的优先级都低于原始转换器,以防止出现任何假设问题。

以上示例对我来说适用于target<a>的当前母版。