bernhard Posted January 23, 2020 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! ?
louisstephens Posted January 23, 2020 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
BitPoet Posted January 23, 2020 Posted January 23, 2020 preg_replace('~(<h[23]\\b.*?>(?:<[a-z][^>]*>)*)(\w+)~ism', '$1<span>$2</span>', $headline); quick&dirty in PHP 2 1
dragan Posted January 23, 2020 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
bernhard Posted January 24, 2020 Author 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
dragan Posted January 24, 2020 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
bernhard Posted January 25, 2020 Author 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?
dragan Posted January 25, 2020 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
tpr Posted January 25, 2020 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
bernhard Posted February 4, 2020 Author 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...
dragan Posted February 4, 2020 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
bernhard Posted February 5, 2020 Author 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:
Recommended Posts