0

Is there any way to use regular expression alone or with help of javascript to do the following

from

<div class="type-c red blue"> 

to

<div type="c" class="red blue"> 
Fan Cheung
  • 10,745
  • 3
  • 17
  • 39

3 Answers3

2

You don't even need regular expressions.

(Okay, you do, but only to find the x value in type-x. Proof here.)

You can use a mix of attribute selectors, the data-* attribute standard, and the Element.setAttribute() method.

Here's an example.

function doChange() {

  // Find all divs with a 'type-x' class
  let myDivs = document.querySelectorAll('div[class*="type-"]');

  myDivs.forEach(curDiv => {

      // Get the specific 'x' for the type
      let curType = /(?<=type-)[A-Za-z\-]+/.exec(curDiv.classList.toString())[0];

      // Set the 'data-type' attribute
      curDiv.setAttribute('data-type', curType);

      // Remove the 'type-x' class
      curDiv.classList.toggle('type-' + curType);

      // Write the 'classList' and 'data-type' attributes for show
      curDiv.innerText = 'classList: ' + curDiv.classList 
                       + '; data-type: ' + curDiv.getAttribute('data-type') + ';';

  });
}
<div class="type-a red blue">classList: type-a red blue; data-type: undefined;</div>
<div class="red type-b blue">classList: red type-b blue; data-type: undefined;</div>
<div class="red blue type-c">classList: red blue type-c; data-type: undefined;</div>

<button onclick="doChange()">Click Me</button>
D M
  • 5,769
  • 4
  • 12
  • 27
  • sorry maybe i should state in the question the ask is for code refactoring – Fan Cheung Jan 20 '21 at 03:53
  • Oh, then I'm obligated to point you at [the famous answer](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454). And suggest you consider if your source is consistent and small enough that `regex` is a good choice. If so, you can use the pattern in my answer to help you find the `type-x` classes in the code and start from there. – D M Jan 20 '21 at 03:56
  • I updated the pattern to use positive-lookbehinds (lookaheads) to help you match more easily. They will look for a single character preceded by `type-` so you don't have to worry about taking the first group instead of the match. – D M Jan 20 '21 at 04:06
  • thanks for the help, i found the solution of regex (type-)(.+?)(?="| ) i make use of these two matching group to replace the code – Fan Cheung Jan 20 '21 at 04:08
  • Can you show that as an answer @FanCheung to your question? – GetSet Jan 20 '21 at 04:11
  • I ask because I am curious on the exact regex – GetSet Jan 20 '21 at 04:16
  • still working on it. i get the matching group the hard part to is to do the replace without coding – Fan Cheung Jan 20 '21 at 04:23
  • Yeah thats what I was wondering on how you achieved it. Perhaps we can combine minds (in @DakotaMethvin sense) and I'll post my also non-regex solution. – GetSet Jan 20 '21 at 04:24
  • @Fan Is the `type-x` class always the first in the class list? If so, you can string replace `class=“type-x ` with `type=“x” class=“`. – D M Jan 20 '21 at 04:25
  • Thats was my approach too @DakotaMethvin. Though I use `replaceAll()` method instead of changing the DOM – GetSet Jan 20 '21 at 04:26
  • not always the first class entry .. lol @Dakota Methvin – Fan Cheung Jan 20 '21 at 04:30
  • Well if its not the first, then that changes things *a lot*. The entire before "class" string will have to be tokenized. – GetSet Jan 20 '21 at 04:31
  • Here’s [the pattern](https://regex101.com/r/myJg4l/1) I would go with to find your class: `(?<=class=")(?:[A-Za-z0-9\- ]*?)(type-[A-Za-z0-9\-]+)(?:[A-Za-z0-9\- ]*)(?=")`. Should be pretty easy from there to drop the class and add the attribute. – D M Jan 20 '21 at 04:51
2

This isn't the solution. Just offers some insights on text parsing this problem without regex. As the OP did mention a possible non-regex solution. Also this answer was posted during a very long comment session, and as such requirements changed.

This solution (in like fashion of @DakotaMethvin's) also attempts to solve it without regex, and as well, resides on a more exact pattern match with class="type-. As such the following code will indeed break (logically) if that's not the match.

However in light of FanCheung's more recent comment [type-x] not always the first class entry, I'm only posting this answer because I already started on posting it.

function replaceIt( s, sentinel ) {
    // sentinel: e.g. class="type-
    if (sentinel) {
    
    }
    else sentinel = 'class="type-';
    
    return s.replaceAll(sentinel , function( match, offset ) {
        
        // get the position of the whitespace after "type-c"
        nextSpace = s.indexOf(" ", offset);
        
        // extract the "type" up until and excluding the "next space"
        typeVar = s.substring( offset + sentinel.length, nextSpace);
        
        // get the position of the 2nd double quote char 
        nextDoubleQuote = s.indexOf("\"", nextSpace);
        
        // extract the new "class" names e.g. red blue
        newClass = s.substring( nextSpace + 1, nextDoubleQuote);
        
        // create the replacement string. Also something must be done with the leftover chars; prepend them with 'data-source'.
        replacement = 'type="' + typeVar + '" class="' + newClass +'" data-source="'; 
        
        // debugging code
        console.log( match + ": " + offset + ": " + typeVar + ": " + newClass);
        
        return replacement;
    });
    
}

console.log( replaceIt( '<div class="type-c red blue">') );

But as noted on recent developments on what I noted on The entire before "class" string will have to be tokenized. So this solution only works if "type-" is the 1st class.

GetSet
  • 1,511
  • 2
  • 10
  • 13
  • @FanCheung Yes thank you too. Was just bout to go back to my excessive non-regex (I did) .. I appreciate it: some tweaks on that non-regex, like 5 deletions of code and 5 additions. Okay really but helps to realize regex is superior in many (or some) cases. Glad you got your answer tho. – GetSet Jan 20 '21 at 12:46
  • ya same here. gonna do some more learning, seems very useful in refactoring – Fan Cheung Jan 20 '21 at 12:48
1

This regular expression will match what you describe, regardless of the position of "type-xxx" in class attribute

/class="([^"]*)type-(\w+)([^"]*)"/g

Combining with a string replace

let value = '<div class="type-a b">test</div><div class="a type-b">test 2</div>';
value.replace(/class="([^"]*)type-(\w+)([^"]*)"/g, 'type="$2" class="$1$3"');

this will yield the result

<div type="a" class="b">test</div><div type="b" class="a">test 2</div>
Dat Pham
  • 1,765
  • 15
  • 13