1. ホーム
  2. Web制作
  3. HTML/Xhtml

HTMLのキーワードをハイライトするための完璧なソリューション

2022-01-08 02:22:26

最近、あるプロジェクトでこの機能に出会いました。Webページ内のキーワードをハイライトする機能です。

単純なinnerHTMLの置換操作と思いきや、いろいろと問題がありました。この記事はそれらの問題と最終的に完璧な解決策を記録し、同じことを経験した人の参考になればと思います。もし結果だけに興味があるのなら、プロセスは無視して結果まで飛ばしてください。

一般的な方法:定期的な置き換え

アイデア 要素をハイライトするには、キーワードを抽出してタグで囲み、そのタグにスタイルを設定する必要があります。innerHTMLやoutHTMLを使用するが、innerText,outTextは使用しない。

notification.setLatestEventInfo(context, title, message, pendingIntent);   

このようなことをすると、次のような落とし穴があります。

Intent intent = new Intent(this,MainActivity);  
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);  
notification.setLatestEventInfo(context, title, message, pendingIntent);          
manager.notify(id, notification);  

キーワードの親要素はクラスによって背景色付けされるため、元のDOMをある程度汚染し、要素の再配置に影響を与える可能性があります。(プラグインとしては、元のDOMをできるだけ変更したくないものです)。

正準最適化I:タグの中にある要素だけを扱う

Notification.Builder builder = new Notification.  
            .setAutoCancel(true)  
            .setContentTitle("title")  
            .setContentText("describe")  
            .setContentIntent(pendingIntent)  
            .setSmallIcon(R.drawable.ic_launcher)  
            .setWhen(System.currentTimeMillis())  
            .setOngoing(true);  
notification=builder.getNotification();  

これで大体の問題は解決しましたが、tag属性に<などの記号がある限り、マッチングルールが破綻して正規に抽出された内容がおかしくなるという問題が残っており、HTML5データセットは任意の内容でカスタマイズできるため、これらの特殊文字を避けることができません。

Notification notification = new Notification.Builder(context)    
         .setAutoCancel(true)    
         .setContentTitle("title")    
         .setContentText("describe")    
         .setContentIntent(pendingIntent)    
         .setSmallIcon(R.drawable.ic_launcher)    
         .setWhen(System.currentTimeMillis())    
         .build();   

正規化最適化 II: 影響を与える可能性のあるタグを削除する

package com.example.mynotification;  
  
import android.app.Activity;  
  
import android.app.Notification;  
  
import android.app.NotificationManager;  
  
import android.app.PendingIntent;  
  
import android.content.Context;  
import android.content.Intent;  
  
import android.os.Bundle;  
  
import android.text.NoCopySpan.Concrete;  
import android.view.View;  
  
import android.widget.Button;  
  
import android.widget.TextView;  
  
public class MainActivity extends Activity {  
  
    Button m_Button1;  
  
    TextView m_txtView;  
  
    NotificationManager mNotificationManager;  
  
    Notification mNotification;  
  
    Intent mIntent;  
  
    PendingIntent mPendingIntent;  
    Context context;  
  
    /** Called when the activity is first created. */  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
  
        super.onCreate(savedInstanceState);  
  
        setContentView(R.layout.activity_main);  
        context = this;  
        final Notification notification;  
        mNotificationManager = (NotificationManager) this  
                .getSystemService(NOTIFICATION_SERVICE);  
  
        m_Button1 = (Button) this.findViewById(R.id.button1);  
  
        // Transfer the content when you click on the notification  
  
        mIntent = new Intent(MainActivity.this, MainActivity1.class);  
  
        mPendingIntent = PendingIntent.getActivity(MainActivity.this, 0,  
                mIntent, 0);  
  
        Notification = new Notification.Builder(context).setAutoCancel(true)  
                .setContentTitle("qq is running").setContentText("qq,make communication more convenient")  
                .setContentIntent(mPendingIntent)  
                .setSmallIcon(R.drawable.ic_launcher)  
                .setWhen(System.currentTimeMillis()).build();  
  
        m_Button1.setOnClickListener(new Button.OnClickListener() {  
  
            public void onClick(View v) {  
  
                mNotificationManager.notify(0, notification);  
  
            }  
        });  
  
    }  
}  

  • このアイデアとソースコードはここから来ているのですが、問題なのは
  • replaced1]にキーワードが含まれていると、置換時に例外が発生する

最も重要なことは、このメソッドは、タグの値に <> 記号が含まれる場合に、タグを正しく抽出できないことです。

とにかく、何度も試行錯誤を繰り返した結果、レギュラー経由では効果的に状況を処理することができませんでした。そこで私は考えを改め、文字列ではなくノードで処理することにしました。element.childNodesは、タグ内部の邪魔な情報を一掃する最も効率的な方法なのです。

[完全解決】DOMノードによる処理

<div id="parent">
    keyword 1
  <span id="child">
    keyword 2
  </span>
 </div>

parent.childNodesで全ての子ノードを取得します。 innerText.replce(keyword,result) を使用すると、次のように希望するハイライト効果を得ることができます。 <span id="child"><b>keyword</b> 2</span> (再帰的処理:子ノードが子を含まない場合の置換操作)。

しかし、キーワード1はテキストノードであり、テキストの内容を変更するだけで、HTMLを追加したり、スタイルを個別に制御したりすることはできません。そして、テキストノードは通常のノードに変換することができないので、それが一番不満です。

最後に~、ここで今回の記事のポイントなのですが、この機能で初めてテキストノードというものに本格的に触れることができました。ここから、カット&リプレースのテキストノードを使ってハイライトを実現するTextを発見したのです。

ソースコードと復元されたハイライトはソースコード内にあります

const reg = new RegExp(keyword.replace(/[-\/\\^$*+? ()|[\]{}]/g, '\\$&'))
highlight = function (node,reg){
    if (node.nodeType == 3) { //process only text nodes
        const match = node.data.match(new RegExp(reg));
        if (match) {
          const highlightEl = document.createElement("b");
          highlightEl.dataset.highlight="y"
          const wordNode = node.splitText(match.index)
          wordNode.splitText(match[0].length); // cut into the first keyword and then three Text nodes
          const wordNew = document.createTextNode(wordNode.data);
          highlightEl.appendChild(wordNew);// highlight node constructed successfully
          wordNode.parentNode.replaceChild(highlightEl, wordNode);// replace the text node
        }
    } else if (node.nodeType == 1 && node.dataset.highlight!="y"
    ) {
        for (var i = 0; i < node.childNodes.length; i++) {
            highlight(node.childNodes[i], reg);
            i++
        }
    }  
}

概要

上記は、HTMLでキーワードを強調するための完璧なソリューションです。私はそれがあなたを助けることを願って、何か質問がある場合は、私にメッセージを残してください、私は時間内にあなたに返信されます。また、Script Houseのウェブサイトをサポートしてくださっている皆様に感謝いたします。