I want to tag the text that matches the regular expression and set the event handler.

Asked 1 years ago, Updated 1 years ago, 27 views

I would like to tag a string of innerText/textContents for an element that corresponds to a specific regular expression (for example, /#\d+/) and set up an event handler.For example, see <p>#123 for that matter.If you hover your mouse over #123 in the HTML called </p>, the corresponding Issue details pop up.

One idea is to replace innerHTML.

 target.innerHTML = target.innerHTML
                   .replace(/#(\d+)/g,(s,id)=>`<a href=".../${id}">${s}</a>`);

You can replace it with a simple link, but if you have more attributes and event handlers to configure, it's troublesome to tag and it's hard to read, but it's a string at this point and you can't access the Element object.

Due to the convenience of creating UserScript, I would like to avoid using class attributes if possible (because it may conflict with the parts used on the target page).

Range.prototype.surroundContents() was available when I did the same thing for the selection, so it would be easy to get the match range in Range again...

javascript html

2022-09-30 15:03

2 Answers

The XPath expression descendant::text()[contains(., "#123")] allows you to obtain a text node containing the string "#123".
However, since the escape sequence of " is not available in XPath 1.0, you can use concat() to help (see the link below for more information).

(Added March 3, 2017 08:00)
I'm sorry. I misunderstood the question.
If you want to match it with a regular expression, you can use XPath expression :matches() or /#\d+/.test(textNode.data) with a while statement after browsing all text nodes

'use strict';
vartoXPathStringLiteral=(function(){
  function replacefn(match,p1){

    if(p1){
      return',\u0027'+p1+'\u0027';
    }

    return', "'+match+'";
  }

  return function toXPathStringLiteral (string) {
    string = String(string);

    if(/^"+$/g.test(string)){
      return '\u0027' + string + '\u0027';
    }

    switch(string.indexOf('"')}{
      case-1:
        return '' '' + string + '' ;
      case0:
        return'concat('+string.replace(/(+)|[^"]+/g, replacefn).slice(1)+')';
      default:
        return'concat('+string.replace(/(+)|[^"]+/g,replacefn)+')';
    }
  };
}());

function handleClick (event) {
  console.log(event.target.textContent);
}

function markupHighlight(targetString, contextNode) {
  var doc=contextNode.nodeType===contextNode?contextNode:contextNode.ownerDocument,
      xpathResult=doc.evaluate('descendant::text()[contains(., '+toXPathStringLital(targetString)+')]', contextNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null),
      df = doc.createDocumentFragment(),
      mark = doc.createElement('mark');

  mark.appendChild(doc.createTextNode(targetString)));

  for (vari=0, xLen=xpathResult.snapshotLength, currentTextNode, stringList, textNode;i<xLen;++i){
    currentTextNode=xpathResult.snapshotItem(i);
    stringList=currentTextNode.data.split(targetString);
    textNode = df.appendChild(doc.createTextNode(stringList[0])));

    for (varj=1, stringLen=stringList.length, textNode;j<stringLen;++j){
      mark = mark.cloneNode(true);
      df.appendChild(mark);
      mark.addEventListener('click',handleClick,false);
      textNode = textNode.cloneNode(false);
      textNode.data=stringList[j];
      df.appendChild(textNode);
    }

    currentTextNode.parentNode.replaceChild(df, currentTextNode);
  }
}

markupHighlight('foo', document.body);
mark{
  color:black;
  background-color:#ddf;
  border —solid1px#55f;
}
<ulid="foo">
  <li>foo1</li>
  <li>foo2</li>
  <li>foo3</li>
  <li>
    <ul>
      <li>foo4-1</li>
      <li>foo4-2foo4-2</li>
      <li>
        <ul>
          <li>foo4-3-1</li>
          <li>foo4-3-2foo4-3-2foo4-3-2</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Reference Links

Dear Re:user204


2022-09-30 15:03

DOM is very bad at manipulating things that are not represented by DOM objects.I mean, I can hardly do it.On the other hand, if you drop it into DOM, you can operate it.
A string in HTML is configured in DOM as a Node object of nodeType===TEXT_NODE, so-called textNode, which can be divided into any length of partial string (no matter how many textNodes the child element has/the first textNode does not match the .textContent).In this approach, we first divide the target string into the textNode of the partial string.
However, textNode does not inherit the EventTarget (Node instance of EventTarget===false), so you cannot register events unlike regular DOM elements (HTMLElement).In order to solve this problem, what we call "replace with HTML elements" is the solution.
Below, the sample code generates and replaces the partial string at the same time.Substring searches extract textNodes by enumerating them by NodeIterator and search, segment, and replace the contents of each textNode.The function __replaceTextNodes returns a reference array to the replaced element, so subsequent applications can use it to register event handlers, etc.Overall, I felt like a polyfill in Range.prototype.surroundContents().

const__getTextNodesByContent=(target, pattern)=>{
    // getiterator
    consttextNodeIterator= document.createNodeIterator(
        // search for
        target,
        // enumerate for
        NodeFilter.SHOW_TEXT,
        // filter
        {
            acceptNode: node=>pattern.test(node.textContent)? NodeFilter.FILTER_ACCEPT: NodeFilter.FILTER_REJECT,
        }
    );

    // into array
    const = [ ];
    let current;
    while(current=textNodeIterator.nextNode()){
        ret.push (current);
    }

    return;
};

const__replaceTextNodes=(target, pattern, replace)=>{
    // get the list of textNodes that contains/pattern/
    const matchedTextNodes=__getTextNodesByContent(target, pattern);

    // for each textnode
    const = [ ];
    for (const node of matchedTextNodes) {
        // replace each part of textNode
        let currentNode=node;
        let matches;

        while(pattern.lastIndex=0, matches=pattern.exec(currentNode.textContent)){
            // separate [ currentNode | <before><match><after>]
            // into [ currentNode | <before>] [ nextNode | <match><after>]
            const nextNode=currentNode.splitText(matches.index);

            // slice <match> part; [ currentNode | <before>] [ nextNode | <after>]
            nextNode.textContent=nextNode.textContent.slice(matches[0].length);

            // insert surround < match > part as a new sibling;
            // [ currentNode | <before>] [ surroundNode | <match>] [ nextNode | <after>]
            const surroundNode=replace(matches[0]);
            nextNode.parentElement.insertBefore(surroundNode, nextNode);

            // store reference
            ret.push(surroundNode);

            // next
            currentNode = nextNode;
        }
    }

    // return a list of surround elements
    return;
};

//
// for example
//
const$target= document.body;
const targetPattern=/Lorem| massa|ridiculus|pellentesque/gi;

const surroundElements=__replaceTextNodes($target, targetPattern, text=>{
    // surround with <span>, colored with red, add title attribute
    constel= document.createElement('span');
    el.textContent=text;
    el.style.color='red';
    el.title='surround with <span>, text content is "'+text+';";';
    return;
});

// event handler filterd by surround elements
window.addEventListener('click', e=>{
    if(surroundElements.includes(e.target)){
        console.log('click', e.target);
    }
});

// or for each surround elements
/*
for (constse of surroundElements) {
    se.addEventListener(evt,fn);
}
*/ 
<h1>Lorem Lorem ipsum color site connector adipiscing 
elit</h1>

<p>Lorem ipsum color sitamet, connectetuer adipiscing 
elit.Aenean commodoligula eget color.Aenean massa 
<strong>strong>/strong>.Cumsocis natoque penatibus 
et magnis disparturious montes, nasceturridiculus 
mus. Donecquam felis, ultricies nec, pellentesque 
eu, pretium quis, sem. Nulla consquat massa quis 
enim.Donecpede justo, fringillavel, aliquet nec, 
volputate get, arcu.Inenim justo,rhoncusut, 
immediatea, venenatis vitae, justo.</p>

<h2>Lorem ipsum color site connector adipiscing 
elit</h2>

<h3>Aenean commodoligula eget color aenean massa</h3>

<p>Lorem ipsum color sitamet, connectetuer adipiscing 
elit. Aenean commodoligula eget color. Aenean massa. 
Cum sociis natoque penatibus et magnis dispartrent 
montes, nasceturridiculus mus. Donecquam felis, 
ultricies nec, pellentsqueueu, pretium quis, sem.</p>

<ul>
  <li>Lorem ipsum color site connector.</li>
  <li>Aenean commodoligula get dolor.</li>
  <li>Aenean mass sociis natoque penatibus.</li>
</ul>

If you have any other questions, such as not the expected behavior, bugs, etc., please comment.

Note:

innerHTMLBe aware that DOM operations due to property changes are extremely costly and risky.My personal opinion is that it should be used only if it cannot be realized without using anything, and I would like to ban it for purposes other than reflection purposes.In the following example, no changes appear to have been made, and all elements below the body have been replaced.

const$target= document.querySelector('#target');
$target.addEventListener('click', e=>console.log(e));

// will be true
console.log($target===document.querySelector('#target'));

// reconstruct; SCRAP AND REBUILD ALL DOM ELEMENTS
document.body.innerHTML = document.body.innerHTML;

// will be false
console.log($target===document.querySelector('#target'));

// now no event listener attached to <div#target>
<divid="target">target element</div>


2022-09-30 15:03

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.