bernhard Posted January 23, 2020 Share Posted January 23, 2020 Hi, I want to wrap the first line of every H2 and H3 in a <span></span> tag so that I can do further CSS styling on that element. The problem is that the markup comes from a CKEditor field, so it can have different versions: <h2>This is a headline</h2> <h2><span>This</span> is a headline</h2> // result <h2 foo="bar">This is a headline</h2> <h2 foo="bar"><span>This</span> is a headline</h2> // result <h2 foo="bar"><strong>This is a headline</strong></h2> <h2 foo="bar"><strong><span>This</span> is a headline</strong></h2> // result Any tips for me? Domdocument? Any 3rd-party dom parser? Or Regex? Thx for your help! ? Link to comment Share on other sites More sharing options...
louisstephens Posted January 23, 2020 Share Posted January 23, 2020 Not sure if this will suit your needs, but in javascript (jquery) you could do: $('h2').each(function() { var $element = $(this); $element.html($element.html().replace(/^(\w+)/, '<span>$1</span>')); }); 1 Link to comment Share on other sites More sharing options...
BitPoet Posted January 23, 2020 Share Posted January 23, 2020 preg_replace('~(<h[23]\\b.*?>(?:<[a-z][^>]*>)*)(\w+)~ism', '$1<span>$2</span>', $headline); quick&dirty in PHP 2 1 Link to comment Share on other sites More sharing options...
dragan Posted January 23, 2020 Share Posted January 23, 2020 (edited) quick, dirty & ready ? $this->addHookAfter('Page::render', function ($event) { if ($this->page->template != 'admin') { // just an example I use in almost every PW site: add responsive wrappers around iFrames: if (strpos($event->return, 'videoWrapper') === false) { $event->return = str_replace("<iframe", "\n<div class='videoWrapper'><iframe", $event->return); $event->return = str_replace("</iframe>", "</iframe></div>", $event->return); } // another example I use in almost every PW site: add responsive wrappers around tables: if (strpos($event->return, 'scrollTable') === false) { $event->return = str_replace("<table", "\n<div class='scrollTable'><table", $event->return); $event->return = str_replace("</table>", "</table></div>", $event->return); } $cleaned = preg_replace('~(<h[23]\\b.*?>(?:<[a-z][^>]*>)*)(\w+)~ism', '$1<span>$2</span>', $event->return); // BitPoet's regex magic $event->return = preg_replace('/<p>\\s*?(<a .*?><img.*?><\\/a>|<img.*?>)?\\s*<\\/p>/s', '\1', $cleaned); // remove default paragraphs around images in CKE } }); Edited January 23, 2020 by dragan added comments to code snippet 1 1 Link to comment Share on other sites More sharing options...
bernhard Posted January 24, 2020 Author Share Posted January 24, 2020 Hi everybody, I want to share what I came up with. Background: The goal was to get a color-accent to every headline on my new project. First thought was to use CSS-only with ::before pseudo element, but the problem is, that the headline is a either a block element (then position absolute left 0 leads to the accent being on the very left even for text-align:center headlines). Or if the headline is inline-block I had some other weird problem that I can't remember right now. Also multiline headlines where tricky. That's why I wanted to add a real element at the beginning of every headline. Wrapping the first word was actually not necessary. I'm adding an empty <i></i> now right behind the opening <h2> or <h3> tag, so the regex get's even simpler. Here's the result: Here's the LESS if someone is interested: // apply style to all elements with tm-fancy attribute // this attribute is added to all h2+h3 via textformatter *[tm-fancy] { font-family: @tm-font-fancy; i:first-of-type { position: relative; } i:first-of-type::before { content: ''; position: absolute; left: -15px; bottom: 5px; width: 120px; height: 20px; background: @tm-secondary; z-index: -1; border-radius: 2px; } } // adjust width and height for h3 h3[tm-fancy] i:first-of-type::before { width: 100px; height: 15px; } And here's the very simple textformatter that I can apply now an every textarea field: <?php namespace ProcessWire; class TextformatterFancyHeadlines extends Textformatter { public static function getModuleInfo() { return array( 'title' => 'FancyHeadlines', 'version' => 100, 'summary' => "Add <i></i> to headlines for color accents.", ); } public function format(&$str) { // early exit if there are not h2 or h3 elements if(strpos($str, "<h2") < 0 AND strpos($str, "<h3") <0) return; // add attribute and inline element via regex $re = '~(<h[23][^>]*)(>)~ism'; $str = preg_replace($re, "$1 tm-fancy$2<i></i>", $str); } } Thx for your help ? PS: If anybody knows a better solution please share it ? Maybe there's a CSS-only way of doing that? 4 Link to comment Share on other sites More sharing options...
dragan Posted January 24, 2020 Share Posted January 24, 2020 3 hours ago, bernhard said: Maybe there's a CSS-only way of doing that? here's a quick try: https://codepen.io/dragan1700/pen/bGNZBEK?editors=1100 Whatever you do: don't ever try to do stuff like "underline only the last line of a text block": it's a major cause of migraine. Without using JS this can't be done (yet) only with CSS alone. (Well - not unless you do PDFs, of course, or don't have to make stuff responsive...). A client recently came up with BS like that (which was designed by another agency), and we had a hard time explaining why this was not a good idea. 2 Link to comment Share on other sites More sharing options...
bernhard Posted January 25, 2020 Author Share Posted January 25, 2020 8 hours ago, dragan said: here's a quick try: https://codepen.io/dragan1700/pen/bGNZBEK?editors=1100 How can I center that headline? Link to comment Share on other sites More sharing options...
dragan Posted January 25, 2020 Share Posted January 25, 2020 Ah, centered! LOL, I knew I was missing something... I'd say, stick with your solution. With frontend only, you'd have to use JS as well. For at least big screens this might work though (updated): https://codepen.io/dragan1700/pen/bGNZBEK?editors=1100 1 Link to comment Share on other sites More sharing options...
tpr Posted January 25, 2020 Share Posted January 25, 2020 I would just probably add a new ckeditor span with a class so editors could apply manually. That would also allow excluding headlines. 1 1 Link to comment Share on other sites More sharing options...
bernhard Posted February 4, 2020 Author Share Posted February 4, 2020 On 1/25/2020 at 3:44 PM, tpr said: I would just probably add a new ckeditor span with a class so editors could apply manually. That would also allow excluding headlines. Hi @tpr, I've just tried to do what you suggested, but I failed ? My first try was to set a custom style, but that is not exactly what I want, because these are inline styles that are applied to the current selection. I want to add a new TAG to the list of format tags: Eg Heading 2 FANCY which would add <h2 class="tm-fancy">...</h2> This did unfortunately not help/work: https://stackoverflow.com/questions/17230809/how-to-add-a-custom-paragraph-format-in-ckeditor Has anybody ever added a custom paragraph format (block element) instead of a custom style? I'll try the inline style solution though... Link to comment Share on other sites More sharing options...
dragan Posted February 4, 2020 Share Posted February 4, 2020 You can simply edit /** * mystyles.js * * This file may be used when you have "Styles" as one of the items in your toolbar. * * For a more comprehensive example, see the file ./ckeditor-[version]/styles.js * */ CKEDITOR.stylesSet.add( 'mystyles', [ { name: 'Inline Code', element: 'code' }, ... { name: 'Heading Underline Span', element: 'span', attributes: { 'class': 'heading_underline' } }, ... ] ); and in your field definition (tab input), add "Styles" to the toolbar. 1 Link to comment Share on other sites More sharing options...
tpr Posted February 4, 2020 Share Posted February 4, 2020 Here I added some code snippets that may help: 1 Link to comment Share on other sites More sharing options...
bernhard Posted February 5, 2020 Author Share Posted February 5, 2020 16 hours ago, bernhard said: My first try was to set a custom style, but that is not exactly what I want, because these are inline styles that are applied to the current selection. I want to add a new TAG to the list of format tags: Yeah, thx for your links, but as I said what I actually tried to do was to add a new paragraph format, not an inline style ? I've managed to add an inline style already. I think that option should also work fine. It's actually a little bit more flexible, because then the client can apply the special font to any tag (H2, H3 and P)... CKEDITOR.stylesSet.add( 'mystyles', [ { name: 'Handschrift', element: 'span', attributes: { 'class':'tm-fancy' } }, ... Extra Allowed Content also needs to be set when ACF is active: Link to comment Share on other sites More sharing options...
Recommended Posts