Option to add class to <a> in Redactor editor - javascript

I am using the Redactor editor as part of Perch. I would like to give the editor the ability to (optionally) add the class of "button" to any <a> tag they add. This could be via an additional option in the existing Add a Link modal, or could be a separate button in the editor toolbar.
Does anyone have any guidance as to the best way to achieve this? Any pointers appreciated.
This is my current config setup:
config.plugins = [];
config.buttons = ['format','bold','italic','deleted','link','lists'];
config.formatting = ['p', 'blockquote', 'h2', 'h3', 'h4'];

You can use formattingAdd to create a custom set of formatting options for Formatting dropdown. This will allow you to add your own CSS classes.
formattingAdd: {
'red-p-add': {
title: 'Red Paragraph',
api: 'module.block.format',
args: {
tag: 'p',
class: 'red-styled',
},
},
}
Unfortunately, according to the Official Documentation:
formattingAdd can only be applied to block tags (p, pre, blockquote, div, heading, etc).
In other words, since <a> is an inline element (not a block element), if you are trying to create an option using built-in Redactor functionality, then it appears you are out of luck.

As noted in other answers, formattingAdd can not be applied to a tags. Here's a plugin for the latest version of redactor that takes highlighted text/links and converts it into an a tag with a specific class you're after:
(function($R) {
$R.add('plugin', 'button', {
init: function(app) {
this.app = app;
},
activate: function() {
// Remove existing link if any
this.app.api('module.link.unlink')
// Add a tag with 'button' class
this.app.inline.format({ tag: 'a', class: 'button' })
}
});
})(Redactor);
With that code defined and loaded, you then need some updates to the redactor json config to pull in the functionality:
// include the plugin to redactor's world
"plugins": ["button"],
...
"formattingAdd": [
...
// add a "Button" option in the formatting dropdown,
// but use it to trigger your own plugin's function
{
"api": "plugin.button.activate",
"args": {
"class": "button",
"tag": "a"
},
"title": "Button"
},
]
I attempted to throw a this.app.api('module.link.open') as the last line of the activate function, so the link modal would be opened up as soon as your link class was activated. It was nice, but I noticed some inconsistent behavior depending on the scope of the initial selection (worked well if just the text was selected, but if a DOM tag portion was also selected the whole thing broked).

I'm not too familiar with Redactor, but perhaps you could you do it with a callback or pure JavaScript after the element loads.
<style>
.my-custom-class-1 {
background-color: red;
}
.my-custom-class-2 {
font-size: 16px;
}
.my-custom-class-3 {
font-family: Helvetica Neue, Helvetica, Arial;
}
</style>
Not sure what is returned from link but if it the html element you may be able to add classes with a callback.
$R('#content', {
callbacks: {
link: {
inserted: function(link) {
// use the classList API to remove and add classes
link.classList.remove("my-custom-class-1");
link.classList.add("my-custom-class-1");
// add or remove multiple classes
link.classList.add("my-custom-class-1", "my-custom-class-2", "my-custom-class-3");
link.classList.remove("my-custom-class-1", "my-custom-class-2", "my-custom-class-3");
}
}
}
});
If the links have a custom id then you may be able to do this in pure JavaScript
const link_1 = document.getElementById('linkId_1');
const link_2 = document.getElementById('linkId_2');
const link_3 = document.getElementById('linkId_3');
// use the classList API to remove and add classes
link_1.classList.remove("my-custom-class-1");
link_1.classList.add("my-custom-class-1");
// add or remove multiple classes
link_1.classList.add("my-custom-class-1", "my-custom-class-2", "my-custom-class-3");
link_1.classList.remove("my-custom-class-1", "my-custom-class-2", "my-custom-class-3");
Hope this helps! :)

I know this is a bit of an old question, but I got tired of coming across the same half-baked solutions in every old thread I came across.
I wrote a plugin that lets you configure a list of class names that content editors can choose from when adding / editing a link.
It works seamlessly in combination with the default Redactor link module.
https://github.com/simplicate-web/redactor-link-styles

Related

How to override Trix HTMLSanitizer to allow span tags with class

I need to allow span tag with class in the trix editor.
I could achive adding span tag by
Trix.config.textAttributes.span = {
tagName: "span",
inheritable: true
};
But I can't get class on span it is still getting stripped by the Trix.HTMLSanitizer I guess.
I also tried with
Trix.config.textAttributes.span = {
tagName: "span",
inheritable: true,
parser: false
};
//and
Trix.config.textAttributes.span = {
tagName: "span",
inheritable: true,
parser: (element) => {
element.allowedAttributes = 'class';
}
};
Can't figure out how to override the Trix.HTMLSanitizer to allow something like <span class="my-class">value</span> to show up styled in the editor.
I had the same need and ended up using a hack to work around the problem: I restyled the .trix-content del element in CSS. This only works if you just need a single custom style, though. (I could do this because I didn't need the strikethrough formatting, so I could (ab)use that tag.)
To make it nicer, I then replaced the del tag with a proper span class="xxx" in the content whenever I needed to display the text anywhere other than in the Trix editor itself. That is, I used del internally to mark up, store and edit/update the content, and swapped it out for a proper span to display to the user outside the editor.
On creation of the rich-text content, to mark it for highlighting:
def create
...
object_params[:content].gsub!(/foo/i, '<del>\0</del>')
...
end
Then, when loading it for display outside the editor:
#new_content = #object.content.to_s.gsub(/<del>/i, '<span class="highlight">').gsub(/<\/del>/i, '</span>').html_safe
And the SCSS:
// Trix-editor
.trix-content {
del {
background-color: $yellow;
text-decoration: none;
}
}
.highlight {
background-color: $yellow;
}
This may not do what you need and is a bit nasty in any case. It would be great if Trix could support arbitrary span tags!
(FWIW, my need was to scan the text file and highlight certain words for the user.)

What CSS Selector to use for Shadow DOM? [duplicate]

I am searching a way to styling shadow DOM from the outside. For example, I would like to set the color of all text in all 'span.special' elements as RED. Including 'span.special' elements from shadow DOM. How I can do this?
Previously there were ::shadow pseudo-element and /deep/ combinator aka >>> for this purpose. So I could write something like
span.special, *::shadow span.special {
color: red
}
But now ::shadow, /deep/ and >>> are deprecated. So, what do we have as a replacement of them?
I did try many methods, including those described here. Since I'm using an external Web Component lib, I don't have access to modify these components. So, the only solution that worked for me was using JS querySelector, like this:
document.querySelector("the-element.with-shadow-dom")
.shadowRoot.querySelector(".some-selector").setAttribute("style", "color: black");
Not the best solution, not suitable for large stylings, but does work for little enchancements.
#John this was tested with Chrome 83.0.4103.116 (still going to test in Safari) and I did for Ionic (v5) ion-toast component. Here is the (almost) real code I used:
import { toastController } from '#ionic/core';
let toastOpts = {
message: "Some message goes here.",
cssClass: "toast-with-vertical-buttons",
buttons: [
{
text: "Button 1",
side: 'end'
},
{
text: "Button2",
side: 'end'
},
{
icon: "close",
side: "start"
}
]
}
toastController.create(toastOpts).then(async p => {
let toast = await p.present(); // this renders ion-toast component and returns HTMLIonToastElement
toast.shadowRoot.querySelector('div.toast-button-group-end').setAttribute("style", "flex-direction: column");
});
There is still no easy way to pierce through the shadow root, but here are 3 ways you can go about it. Just keep in mind that you will need to make changes inside the web component.
Using variables v1 - You will need to pass the property and consume the variable inside the web component.
Using variables v2 - You will need to consume the variable inside the web component.
Using ::part() - You will need to add a part attribute to the element you want to style in the web component. (Note: this pseudo element is well supported but is still in experimental mode, so make sure you're aware of that before using it in production).
Run code sample below for details.
const elA = document.querySelector('custom-container-a');
const shadowRootA = elA.attachShadow({mode:'open'});
shadowRootA.innerHTML = '<style>:host([border]) {display:block;border: var(--custom-border);}</style>'+
'<p>Shadow content A</p>'
const elB = document.querySelector('custom-container-b');
const shadowRootB = elB.attachShadow({mode:'open'});
shadowRootB.innerHTML = '<style>p {display:block;color: var(--custom-color, blue);}</style>'+
'<p>Shadow content B</p>'
const elC = document.querySelector('custom-container-c');
const shadowRootC = elC.attachShadow({mode:'open'});
shadowRootC.innerHTML = '<p part="paragraph">Shadow content C</p>'
/* Normal way of styling */
p {
color: orange;
}
/* Using variables version 1 */
custom-container-a {
--custom-border: 3px solid gold;
}
/* Using variables version 2 */
custom-container-b {
--custom-color: green;
}
/* Using ::part() */
custom-container-c::part(paragraph) {
color: magenta;
}
<p>Light content</p>
<custom-container-a border></custom-container-a>
<custom-container-b></custom-container-b>
<custom-container-c></custom-container-c>
You could use #import css as explained in this answer to another question on SO.
Include the rule inside the style element in the shadow tree.
<style>
#import url( '/css/external-styles.css' )
</style>
Note that the >>> combinator is still part of the CSS Scoping Module Draft.
Well, #import is not a solution if you are working with library web component that you can't change ...
Finally I found several ways to do it:
1) Cascading. Styles of Shadow DOM's host element affect Shadow DOM elements also. Not an option if you need to style a particular element of the Shadow DOM, not every.
2) Custom properties https://www.polymer-project.org/1.0/docs/devguide/styling
If an author of the web component provided such.
3) In Polymer, the have Custom Mixins also https://www.polymer-project.org/1.0/docs/devguide/styling
4) #import, but only for not-library components
So, there are several possibilities, but all of them are limited. No powerful enough way to outside styling as ::shadow were.

Select2 Tags: Dynamicly add tag to input

I am looking for a solution to add a tag to the input tag field.
I know it is possible to have preloaded tags, but I have found no way to add a tag to the input. I.e. using a specific function, I could append a selection (with ID and TEXT).
I have searched quite a bit, but maybe I'm in a dead loop. Any suggestions?
This is how I initialize:
$('.select2Input').select2({
tags: {
0: {
id: 'the id',
text: 'the text'
}
},
multiple: true,
minimumInputLength: 2
});
As i mentioned in this related question select2 destroy and recreate, the trick lies in destroying and recreating the select2 when you need to change the initialization options. i suspect that this is what you need to do in your case as well.
the basic syntax is
$('#categoryid').select2("destroy");
createmycategoryidselect();
function createmycategoryidselect() {
// standard options
$opts = { ... }
// gather the current set of tag values and stick them in this new instance.
$opts.tags = { ... new tags ... }
}

Apply style on insert into div

I'm building a search by tags input box as seen here:
http://jsfiddle.net/Newtt/7nUAf/
Forgive the terrible styling as this is just a small component of a larger application and I've just added the styles needed to show my issue.
My search box is a div that has it's text inserted using Jquery as follows:
$(document).ready(function () {
$('.search-box').click(function () {
$('.search-options').toggle();
});
$('.options').click(function () {
var d = $('.search-box').html();
console.log(d);
var c = $(this).html();
console.log(c);
if (d != '') {
$('.search-box').html(d + ', ' + c);
} else {
$('.search-box').html(c);
}
$('.search-options').hide();
});
$('#reset').click(function () {
$('.search-box').html('');
});
});
where .search-box is the input div, .options are the clickable options from the drop down box search-options.
Currently, the text of each option is inserted into the search-box div. I need this to be styled dynamically while it enters the search box.
I tried something on the lines of:
$('<span>').addClass('tag').append(
$('<span>').text(value).append(' '),
$('<a>', {
href : '#',
title : 'Removing tag',
text : 'x'
});
where the tag class is defined in the style sheet to style the element to look like a tag,
but this doesn't work at all. Can someone help me out with how to achieve styling the input text to look like a tag from, say, Evernote notebooks?
Thanks!
I adapted your fiddle. Just wrap c in a span with a class (like you were trying to do in the second part of your post) and apply styles in css. I have just made the background red, but it should be easy enough to make it look like a tag like the ones in the drop down do.
http://jsfiddle.net/7nUAf/1/
JS:
$('.options').click(function () {
var d = $('.search-box').html();
var c = $(this).html();
$('.search-box').append('<span class="tag">'+c +'</span>');
$('.search-options').hide();
});
CSS:
.tag {
background: red;
}
For what you are looking to do - there are lots of excellent plug ins already available that provide much "prettier" functionality and with much less work on your part. Some have already been suggested in the comments - I might suggest consider using "chosen". The syntax is amazingly simple. Just create a select box as follows:
<select id="test" multiple>
<option>pdf</option>
<option>document</option>
</select>
Then in your document ready function you simply need to call chosen plugin:
$(document).ready(function () {
$('#test').chosen({width: "80%"});
});
I put together an example that does this on JSFiddle here: http://jsfiddle.net/7nUAf/3/. Once you get to the point that you have it working you can easily style the elements by inspecting what elements chosen is creating. For example the "li.search-choice" selector will allow you to style the selected items.
In General - even if you don't like this particular plug in, always consider running a search for existing items that do what you are looking for. In the case that these aren't perfect you can always improve them and provide that insight back to the community as a whole. In that way, everyone learns together.
Best of luck!

CodeMirror 2 - Highlight only (no editor)

Can CodeMirror 2 be used to highlight code from a DIV or PRE tag (without the editor)?
Like CodeMirror 1 used to be able to do with the hightlightText() function?
For example here: http://codemirror.net/1/highlight.html, after you press run highlight (the highlighted text below)
Also can it highlight code from a inline element, like <code>, and keep the results inline, like Google's Prettify does?
A much nicer and easier solution is to just set the readOnly property of the CodeMirror instance to true, like this:
$('.code').each(function() {
var $this = $(this),
$code = $this.html();
$this.empty();
var myCodeMirror = CodeMirror(this, {
value: $code,
mode: 'javascript',
lineNumbers: !$this.is('.inline'),
readOnly: true
});
});
Just add the class .code to the tag containing the code and it will be syntax highlighted. I've also added support for inline code, by using the class .inline.
Example on jsfiddle
As a somewhat late update, CodeMirror 2 recently gained this ability. See http://codemirror.net/demo/runmode.html
You should use a standalone code syntax highlighter: SyntaxHighlighter 3 works really well.
If you really want CodeMirror, there is a readOnly option:
var myCodeMirror = CodeMirror(function(elt) {
myElement.parentNode.replaceChild(myElement, elt); // myElement is your <pre> or <div>
}, {
value: myElement.value,
readOnly: true
});
Actually you can't. Codemirror2 is written in the way that all implementation is hidden in closures. Public methods which can be used are described in documentation http://codemirror.net/manual.html
The only available options are to use anothe syntax highlighters or dive into the code of CodeMirror2 to strip necessary parts out.
If you will chose last option, please give attention to
function refreshDisplay(from, to) method
it loops through lines and highlights them.
Edit
Just realized a simpler method exists. Read method 2 below. I'm keeping the old method and its explanations intact and just include the improved jQuery code.
If you are asking about a native method of the package, the answer is no, it only works with textarea. But if you are open to using workarounds, here is one that works (tested).
I have used jQuery here, but its use is not a must and you can achieve the same with pure js code, though it would be longer and not as neat as jQuery code.
Now, let's get to the workaround.
Suppose you have a <pre> with code inside, that you want to turn into editor-less syntax-highlighted codemirror container:
<pre id="mycode">
<?php
echo 'hi';
$a = 10;
if($a == 5) echo 'too small';
?>
</pre>
What you do is,
change the <pre> to <textarea>,
attach codemirror to the textarea,
hide the fake cursor and keep it hidden, and
do not allow the hidden codemirror's textarea grab the focus (and snatch it back when it does).
For the last action I have used the method suggested by Travis Webb. Here is the jQuery code that does these four things:
$(document).ready(function() {
// (1) replace pre with textarea
$('#mycode').replaceWith('<textarea id="code">' + $('#mycode').html() + '</textarea>');
// (2) attach codemirror
var editor = CodeMirror.fromTextArea($("#code"), {
lineNumbers: true,
mode: "application/x-httpd-php"
});
// (3) hide the fake cursor
$('pre.CodeMirror-cursor').hide();
// [4] textarea to grab and keep the focus
$('body').append('<textarea id="tricky" style="height: 1px; position: fixed; width: 1px; top: 0; margin-top: -100px;" wrap="off"></textarea>');
// (4) grab focus
$('#tricky').focus();
// [4] if focus is lost (probably to codemirror)
$('#tricky').blur(function() {
// (4) re-claim focus
$('#tricky').focus();
// (3) keep the fake cursor hidden
$('pre.CodeMirror-cursor').hide();
});
});
Method Two
Instead of wrestling with cursor and all that, we can remove the elements that make the editor tick. Here is the code:
$(document).ready(function() {
$('#mycode').replaceWith('<textarea id="code">' + $('#mycode').html() + '</textarea>');
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "application/x-httpd-php"
});
$('pre.CodeMirror-cursor').remove();
$('div.CodeMirror').find('textarea').blur().parent().remove();
$('div.CodeMirror').find('pre:first').remove();
$('textarea#code').remove();
});
CodeMirror V2 contains a runmode.js.
I've wrote an example using runmode with gutter.
check:
http://jsfiddle.net/lyhcode/37vHL/2/
Heres an simpler solution using codemirror runmode and jquery:
<pre class='code'>{:message => 'sample code'}</pre>
$(document).ready(function() {
$('.code').each(function(index, e) {
$(e).addClass('cm-s-default'); // apply a theme class
CodeMirror.runMode($(e).text(), "javascript", $(e)[0]);
});
});

Categories

Resources