Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting link's target attribute (allow opening links in new tabs) #4829

Closed
itelmenko opened this issue Mar 19, 2018 · 38 comments · Fixed by ckeditor/ckeditor5-theme-lark#233 or ckeditor/ckeditor5-link#222
Assignees
Labels
domain:ui/ux This issue reports a problem related to UI or UX. package:link status:discussion type:feature This issue reports a feature request (an idea for a new functionality or a missing option).
Milestone

Comments

@itelmenko
Copy link

Original ticket description

Hello!

There are not ability to select target of link in ckeditor5 .

Please, add it

Feedback needed

Please read this discussion starting from this comment and leave your feedback (by adding a reaction) in this comment.

@oleq
Copy link
Member

oleq commented Mar 20, 2018

Hi, @itelmenko. We consider implementing this feature in the future. There is no strong ETA, though.

All we can do at this moment is add the target attribute to all links in the editor view and output data by overriding the default downcast converter.

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import {
	downcastAttributeToElement
} from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';

const linkElementSymbol = Symbol( 'linkElement' );
const linkTarget = '_blank';

class MyLinkTargetPlugin extends Plugin {
	init() {
		const editor = this.editor;

		// Override the default downcast converter provided by the LinkEditing class.
		editor.conversion.for( 'downcast' )
			.add( downcastAttributeToElement( {
				model: 'linkHref',
				view: createLinkElement,
				priority: 'high'
			} ) );
	}
}

function createLinkElement( href, writer ) {
	const linkElement = writer.createAttributeElement( 'a', {
		href,
		target: linkTarget
	}, { priority: 5 } );

	writer.setCustomProperty( linkElementSymbol, true, linkElement );

	return linkElement;
}

Then you can simply use the plugin in editing initialization

ClassicEditor
	.create( document.querySelector( '#editor' ), {
		plugins: [ ..., MyLinkTargetPlugin ]
	} );

What sort of UI would you expect to do configure the link target?

@oleq
Copy link
Member

oleq commented Mar 20, 2018

BTW: We're working on some conversion improvements to make the code shorter and to avoid overriding the default converter (which is not safe in the long run).

cc @jodator @scofalik

@itelmenko
Copy link
Author

itelmenko commented Mar 21, 2018

Thanks @oleq for snippet!
But I can not try it because I have not

 '@ckeditor/ckeditor5-core/src/plugin';
 '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';

How can I install it?

Also it is interesting issue:
When I run npm run watch it's OK.
But if I run npm run production I get:

/js/backend/message/editor.1a1e6e866c5e902a4770.js from UglifyJs
Unexpected character '`' [./~/@ckeditor/ckeditor5-build-classic/build/ckeditor.js:5,6550][/js/backend/message/editor.1a1e6e866c5e902a4770.js:93,6517]

I use Laravel 5.6 (where laravel-mix use webpack 2.7.0)

Is it webpack issue or ckeditor?

@oleq
Copy link
Member

oleq commented Mar 21, 2018

Thanks @oleq for snippet!
But I can not try it because I have not

'@ckeditor/ckeditor5-core/src/plugin';
'@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';
How can I install it?

Check out this guide https://ckeditor5.github.io/docs/nightly/ckeditor5/latest/builds/guides/development/installing-plugins.html

Also it is interesting issue:
When I run npm run watch it's OK.
But if I run npm run production I get:

/js/backend/message/editor.1a1e6e866c5e902a4770.js from UglifyJs
Unexpected character '`' [./~/@ckeditor/ckeditor5-build-classic/build/ckeditor.js:5,6550][/js/backend/message/editor.1a1e6e866c5e902a4770.js:93,6517]
I use Laravel 5.6 (where laravel-mix use webpack 2.7.0)

Is it webpack issue or ckeditor?

We also noticed certain issues with UglifyJs in https://github.com/ckeditor/ckeditor5-dev/issues/371 (Webpack 4.x uses it by default). ATM I'd rather recommend you to use babel-minify-webpack-plugin which works just fine.

@ssougnez
Copy link

Hi,

I just found this topic while wondering how come it was not possible to add target to a link.

@oleq, you asked what kind of UI was expected for this. Why not a simple drop down with the posssible values (https://www.w3schools.com/tags/att_a_target.asp).

I guess the first implementation of this feature might not include framename as almost no one uses frame anymore anyway.

This seems to me like a mandatory feature and when you see the awesome job you did for tables, it’s really a shame that links miss such a basic functionality.

I really hope someone will find the time to implement this feature.

@imkane
Copy link

imkane commented Jan 8, 2019

I'm surprised that not every single user of CKEditor would like the option to open links in a new tab. Otherwise, every link in your app will break out of the app. Isn't this like a mandatory feature?

@Reinmar
Copy link
Member

Reinmar commented Jan 8, 2019

I'm surprised that not every single user of CKEditor would like the option to open links in a new tab. Otherwise, every link in your app will break out of the app. Isn't this like a mandatory feature?

It depends.

  • links within the website does not need to (and often should not have) this attribute,
  • links to external websites or out of an SPA should have this attribute.

CKEditor 5 can't decide automatically what should be the behaviour. Therefore, there are three options:

  • it could be configurable which links (based on URL) are produced by CKEditor 5 with target=blank,
  • target=blank could be added automatically by the backend system (when outputting the content to the target page).
  • users can decide (from UX perspective it's a rather bad option, but needed in some cases)

The first option is something that we can implement, of course, and perhaps will. We just don't have this on the roadmap yet.

The second option is what many CMS does. It's actually the best. The content output from CKEditor 5 is treated as abstract data. This data is stored in HTML format, but that's just a format (e.g. Markdown doesn't let you define target=blank as well). But from the abstraction perspective, links have no such property as how they should be opened – that's not to be defined by the data itself. That's to be defined by the system which renders that content to the end users – i.e. your backend systems.

@ssougnez
Copy link

ssougnez commented Jan 8, 2019

I'm a bit confused by this explanation. Most of the time, ckeditor will be used to allow one or several users to enter and save content.

Therefore it is the user who decides whether a sentence is bold, underlined or... a link opens in a new window or not.

How could the system know whether the user wants the link to open in a new tab or not? If the underlying system should be able to do that, why wouldn't it be able to also decide whether a text is bold or not ? At the end, it's the user who decide, not the system (at least in my opinion but I could be wrong), otherwise, the whole formatting features would bebuseless.

@imkane
Copy link

imkane commented Jan 8, 2019

A follow up question ...

This feature was available in CKEditor 4. Why was it removed from v5? Or was it just somehow decided that it wasn't important to implement early on?

@Reinmar
Copy link
Member

Reinmar commented Jan 9, 2019

Our opinion

How could the system know whether the user wants the link to open in a new tab or not?

The question is – how can the user know that? Usually, normal users don't understand and/or care about such things. Plus, a system can do things consistently, based on predefined rules. Users won't do that. If you want all external links to be opened in a new tab (which is what most people want), you can automate this. As I see it this is the most common scenario and therefore that'd be our first step.

However, I understand that in some specific scenarios where these really are users who make the final decision it needs to be possible to control this attribute via the balloon. That's also a feasible extension, but I'd consider this a second step.

This feature was available in CKEditor 4. Why was it removed from v5? Or was it just somehow decided that it wasn't important to implement early on?

A bit of both. When implementing CKEditor 5 we had to review the existing features and rethink most of them. CKEditor 4 (and CKEditor 3 and FCKeditor) have a long history of adding WYSIWYG features in a way which works for developers and for building websites, but makes less sense for normal users and from content semantics perspective. CKEditor 4 started turning into the WYSIWYM direction around 2013 and CKEditor 5 just follows that. It's a rich-text editor which allows WYSIWYM editing. The behaviour of links is just not part of content semantics and a technical detail which most users wouldn't normally care. Also, this happens to be in line with other modern editors – Quill, Prosemirror or Slate don't let you set the target as well. Markdown doesn't allow this too.

But I said "a bit of both" because I don't say we would never implement it. It's just something that's nice to have (because in some cases it may be necessary), but wasn't there in the roadmap to 1.0.0.

Feedback needed

What I wrote above is how we think about it. We may be wrong regarding the priorities so, please vote:

  • Add ❤️ if you want an abiity to configure CKEditor 5 which links should be output with target=_blank and which not. This would be an automatic behaviour, based on link URL. For instance, you could configure CKEditor 5 that all external links are produced with target=_blank.
  • Add 🎉 if you want a "Open link in new tab" checkbox in the link balloon.
  • Leave a comment if you think it should work differently.

Before voting please read this discussion starting from this comment up to this one.

@Reinmar Reinmar changed the title Set default target for links or (better) edit target in balloon Allow setting link's target attribute (allow opening links in new tabs) Jan 9, 2019
@imkane
Copy link

imkane commented Jan 9, 2019

It's sorta funny you talk about other modern editors not having the link target feature, but you fail to talk about BY FAR the most common one out there ... the one that runs 30% of websites ... WordPress. If they ever got rid of link target ability there'd be riots lol.

For anyone like myself using CKEditor in a SaaS, setting an optional link target is absolutely crucial. I can think of numerous other applications where it would be as well. I'm still quite surprised you haven't received many, many more complaints about this.

You can probably tell that I of course voted for the "open link in a new tab" checkbox :)

@FFohlin
Copy link

FFohlin commented Jan 10, 2019

@Reinmar To have our backend parse every text and rewrite the a elements is not an option.

@daveh02
Copy link

daveh02 commented Jan 11, 2019

@Reinmar When is the voting closed and the outcome presented ? When can we expect the checkbox option ?🙂

@Reinmar
Copy link
Member

Reinmar commented Jan 25, 2019

@Reinmar
Copy link
Member

Reinmar commented Jan 25, 2019

@Reinmar When is the voting closed and the outcome presented ? When can we expect the checkbox option ?🙂

Well :trollface: I saw many votes on the checkbox option coming from Swedes (hello from Poland, BTW ;) and newly registered users on the very first day after I asked the question. Normalizing the results with that in mind I'd say it's a draw. Most importantly though, both options got votes, which means that both are worth being implemented. When? IDK yet.

@Reinmar
Copy link
Member

Reinmar commented Jan 25, 2019

@Reinmar To have our backend parse every text and rewrite the a elements is not an option.

Can I ask why?

@FFohlin
Copy link

FFohlin commented Jan 25, 2019

@Reinmar To have our backend parse every text and rewrite the a elements is not an option.

Can I ask why?

Sure. We are using blank for different reasons. We are not able to use the url to determine if the blank should be used or not. internal or external links (or other url parts) does not help us.
Futhermore we try to have the wysiwyg editor to show the result as clearly as possible, and we would have to both parse the html backend, and write a little script to tell the content editors what they can expect in result. Very often they do not see the result on the site when creating the article.

In our company this became a problem for quite a few. And some of them even registered here maybe :)

Keep up the good work, we really like changes made between 4 and 5!
(I think more table functionality is on the wish list for our editors soon)

@imkane
Copy link

imkane commented Jan 25, 2019

This is exactly the same reason why users of our SaaS need the option to set certain links (but not all links) to open in a new tab.

Sure. We are using blank for different reasons. We are not able to use the url to determine if the blank should be used or not. internal or external links (or other url parts) does not help us.
Futhermore we try to have the wysiwyg editor to show the result as clearly as possible, and we would have to both parse the html backend, and write a little script to tell the content editors what they can expect in result. Very often they do not see the result on the site when creating the article.

FYI we've had to revert back to CKEditor 4 as this lack of functionality is a deal-breaker.

@ssougnez
Copy link

As everyone seems to give use case for this, I'll add mine. We are using umbraco cms to allow content managers to create the content. However the site has some dynamic component made in angularjs. The problem is then that some sentence displayed by these content needs to be translated (text like "no result found". So I use the balloon editor to enable content manager to edit these label on the fly.

In this label, managers can add link but the must be able to specify how the link opens...

@oleq
Copy link
Member

oleq commented Apr 9, 2019

Related #1514.

@mlewand
Copy link
Contributor

mlewand commented Apr 10, 2019

We actually would like to provide means to solve both approaches:

  • automatic - as it is very convenient to use if you're able to define the rules properly
  • manual - it provides end user with maximal control of the feature

While thinking about API, this feature can actually be generalized and not limited to assigning targets only. Other use cases include a[download], a[rel], a[ping] attributes. For instance one might want to force download for all links that are contained in, say, /uploads/ directory.

So the common feature sounds more like link decorators.

Config

config.link.decorator = [ 
	{
		// Mode could be either manual or automatic.
		mode: 'automatic',
		callback: function( url ) {
			// This should be a more precise regexp.
			return url.startswith( 'https://' );
		},
		// This is a set of attribute applied to the link in view.
		attributes: {
			rel: 'noopener noreferrer',
			target: '_blank'
		}
	},
	{
		mode: 'manual',
		label: 'Force download', // Label for the checkbox.

		attributes: {
			download: 'download'
		}
	},
	{
		mode: 'automatic',
		callback: function( url ) {
			return url.includes( '/gallery/' ) && url.endswith( '.png' );
		},
		attributes: {
			class: 'lightbox'
		}
	}
];

The config to manually handle a[target=_blank] would be the following:

config.link.decorator = [ {
	mode: 'manual',
	label: 'Open in a new window',

	attributes: {
		target: '_blank',
		manualTarget: true
	}
} ];

UI

Manual decorators should add a checkbox to the UI. We don't expect to extend this feature to provide other input controls (selects/radios).

The simplest idea would be just to put them below the url input:

Editor with the link balloon menu open

One thing to remember here is to make sure that the tab order make sense after adding these controls.

Model

In case of manual decorators the feature will need to place the state in editor model. Simply generating a new unique attribute for each decorator sounds reasonable. So that e.g. linkDecorator1 attribute results with a link that has [target=_blank].

There's no such need for the automatic decorators, as they're stateless and should be queried each time a model item is being converted to a view.

Default behavior

An open question remains whether we need to enable this by default? After thinking this case I believe that adding automatic decorator that would add a[target=_blank] to any external link might be surprising. It sounds better for me to make this an opt-in.

In that case our documentation should contain listings for both solutions.

@Reinmar
Copy link
Member

Reinmar commented Apr 11, 2019

It sounds better for me to make this an opt-in.

In that case our documentation should contain listings for both solutions.

👍 for opt-in and samples of as many solutions as possible.

Other notes:

  • config.link.decorator -> config.link.decorators (cause there are many)
  • 'Open in a new window' needs to be translatable – we should document it clearly that if you'll use this string precisely, then it will be translated to the chosen language.
  • Finally, I think that it should be 'Open link in new tab', just like the tooltip for the link URL in the link balloon.

@msamsel
Copy link
Contributor

msamsel commented Apr 15, 2019

@mlewand, @Reinmar
I'm wondering how specific cases should be covered with manual decorators and ranged selection.

As I can see there isn't displayed any UI for range selection on links, even if those are entirely inside single link. However it's possible to execute command on such range, and behaviour in such cases really depends from selection range.

Should this feature support somehow ranged selection, or be available exclusively for collapsed one and link creation from toolbar?

@Reinmar
Copy link
Member

Reinmar commented Apr 15, 2019

It should do exactly the same thing which the link command does. It must be 1:1 synced with it. In fact, I think that perhaps we should extend the link command here to ensure it always applies all the attributes.

oleq referenced this issue in ckeditor/ckeditor5-theme-lark Jun 25, 2019
Feature: Introduced styles for the decorators UI of the `LinkFormView` component (see ckeditor/ckeditor5-link#186).
oleq referenced this issue in ckeditor/ckeditor5-link Jun 25, 2019
Feature: Introduced configurable link decorators allowing customization of link attributes in the editor data. Closes #186.
@mlewand
Copy link
Contributor

mlewand commented Jun 26, 2019

This feature just got merged to the master branch 🎉 You'll be able to set [target=_blank] (and any other attributes) just by changing the configuration using link decorators feature.

Basic configuration should looks something like that:

ClassicEditor
	.create( document.querySelector( '#editor' ), {
		plugins: [ Link, Typing, Paragraph, Clipboard, Undo, Enter ],
		toolbar: [ 'link', 'undo', 'redo' ],
		link: {
			decorators: {
				isExternal: {
					mode: 'manual',
					label: 'Open in a new tab',
					attributes: {
						target: '_blank'
					}
				}
			}
		}
	} );

More info will be available in the docs.

Sample screencast:
screencast showing open in a new tab switch added to the link panel

@yura-tovt
Copy link

yura-tovt commented Jun 27, 2019

Hi @mlewand. Thanks for implementing this feature. But it seems that upcast conversion for additional attributes is missing.

@msamsel
Copy link
Contributor

msamsel commented Jun 28, 2019

Hi @mlewand. Thanks for implementing this feature. But it seems that upcast conversion for additional attributes is missing.

I'm on it. As I talk with @Reinmar it's important issue. It's reported here as well ckeditor/ckeditor5-link#233.

@mlewand
Copy link
Contributor

mlewand commented Jun 28, 2019

Thanks for catching that! We're working hard to include it in the upcoming release, keep an eye on ckeditor/ckeditor5-link#233 for quickest update.

@Nivekul
Copy link

Nivekul commented Jul 12, 2019

I followed this tutorial and added the link: { addTargetToExternalLinks: true } configuration. But it did absolutely nothing, is there something I'm missing?

I'm using CKEditor5 Classic Build in Vue, version 12.3.1.

@oleq
Copy link
Member

oleq commented Jul 15, 2019

@Nivekul What data (editor content) did you use?

@Nivekul
Copy link

Nivekul commented Jul 16, 2019

@Nivekul What data (editor content) did you use?

@oleq I'm not sure what is the data (editor content), but the following is how I setup the editor

Template

<ckeditor v-model="article" class="editor" :editor="editor" :config="editorConfig"
          @focus="editorFocused=true" @input="handleEdit"></ckeditor>

Data

article: '',
editor: ClassicEditor,
editorConfig: {
    toolbar: {
        items: [
            'Heading', '|',
            'bold', 'italic',
            'link', 'blockQuote',
            'ImageUpload',
            'MediaEmbed',
        ]
    },
    link: {
        addTargetToExternalLinks: true,
    },
    image: {
        toolbar: ['imageStyle:alignLeft', 'imageStyle:full', 'imageStyle:alignRight', '|', 'imageTextAlternative'],
        styles: ['full', 'alignLeft', 'alignRight'],
    },
    placeholder: 'Start writing your article here ...',
    extraPlugins: [(editor) => this.uploadAdapter(editor, this)],
},

@oleq
Copy link
Member

oleq commented Jul 16, 2019

So what happens to links starting with "https://" or "http://" or "//" in your content?

@Nivekul
Copy link

Nivekul commented Jul 16, 2019

@oleq I see, it works now! Thanks!

@mlewand mlewand transferred this issue from ckeditor/ckeditor5-link Oct 9, 2019
@mlewand mlewand added this to the iteration 25 milestone Oct 9, 2019
@mlewand mlewand added domain:ui/ux This issue reports a problem related to UI or UX. status:discussion type:feature This issue reports a feature request (an idea for a new functionality or a missing option). package:link labels Oct 9, 2019
@zehawki
Copy link

zehawki commented Nov 7, 2019

I felt compelled to come here and add a note that this feature is the cat's whiskers :-) What a fricking lifesaver to be able to put an auto rule that sends external links to a new window without having to worry about writers screwing the UX for readers. Thank you! CK editor is lovely, and thank you for listening to the community.

@sdavid501
Copy link

link: {
defaultProtocol: 'http://',
addTargetToExternalLinks: true
}

@Madejczyk
Copy link

@mlewand do you have somewhere branch with your changes mentioned here: #4829 (comment)
I would like to check config of decorators.

@mlewand
Copy link
Contributor

mlewand commented Nov 18, 2021

@Madejczyk I haven't set up any particular branch containing this configuration. All that is required is just some simple config adjustment.

I made a demo so you can play with it: https://codepen.io/mlewand/pen/NWvooJg?editors=1010

@MrBeeUser
Copy link

MrBeeUser commented Jan 2, 2023

link: {
            decorators: {
                openInNewTab: {
                    mode: 'manual',
                    label: 'Open in a new tab',
                    attributes: {
                        target: '_blank',
                        rel: 'noopener noreferrer'
                    }
                }
            }
        }

See here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:ui/ux This issue reports a problem related to UI or UX. package:link status:discussion type:feature This issue reports a feature request (an idea for a new functionality or a missing option).
Projects
None yet