<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pavel Borisov</title>
    <description>The latest articles on DEV Community by Pavel Borisov (@pavelcraft).</description>
    <link>https://dev.to/pavelcraft</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4187796%2Fad9ef788-f900-4203-af5c-a98e07554e8c.jpg</url>
      <title>DEV Community: Pavel Borisov</title>
      <link>https://dev.to/pavelcraft</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pavelcraft"/>
    <language>en</language>
    <item>
      <title>I tested the existing Chrome tab audio recorders. I had to build a proper one</title>
      <dc:creator>Pavel Borisov</dc:creator>
      <pubDate>Sat, 14 Nov 2026 05:23:07 +0000</pubDate>
      <link>https://dev.to/pavelcraft/i-tested-the-existing-chrome-tab-audio-recorders-i-had-to-build-a-proper-one-1mil</link>
      <guid>https://dev.to/pavelcraft/i-tested-the-existing-chrome-tab-audio-recorders-i-had-to-build-a-proper-one-1mil</guid>
      <description>&lt;p&gt;The first version of this article was written for habr, a Russian technology publication, but its moderators rejected it for “using AI to write and edit the text.” So I rewrote it myself. I had honestly tried to protect readers from my own literary talent, but that attempt failed. You will now have to endure my jokes, questionable metaphors, and occasional bouts of verbosity. You can still close the page, though. You have been warned.&lt;/p&gt;

&lt;p&gt;Now to the point. Some time ago, I needed a Chrome extension that could record audio from a browser tab. The task looked so simple that I had no intention of developing anything. Surely the Chrome Web Store already contained dozens of ready-made solutions. Pick one, press Record, and carry on with your life.&lt;/p&gt;

&lt;p&gt;I installed several extensions, tested what they could do, and read their user reviews. Gradually, I came to the conclusion that the world apparently needed my intervention.&lt;/p&gt;

&lt;p&gt;Some extensions were too basic. Others offered useful features but did not always deliver them reliably. Some lacked automation, some raised uncomfortable questions about whether a recording would survive, and some imposed limits whose purpose I could not understand. I will go through those problems below. In the end, I decided to build my own extension and called it Pulse: Free Tab Audio Recorder.&lt;/p&gt;

&lt;p&gt;At first, I planned to make a convenient recorder with a few extra features. Then more ideas appeared. Then a few more. Eventually it became clear that the original plan could be discarded. That is a familiar development story: if someone claims their finished product matches the original specification perfectly, they may simply not have started building it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  An hour of recording, and nothing was saved. Wonderful
&lt;/h2&gt;

&lt;p&gt;I will start with the kind of software that irritates me most: software that merely looks as if it is working. Imagine recording a one-hour lecture. The extension shows a recording indicator, the timer counts every minute correctly, and everything appears fine. An hour later, you click Stop and expect a file. Instead, you get an error. Or an export that never finishes. Or a browser crash. This is particularly delightful when the lecture was a one-off live stream.&lt;/p&gt;

&lt;p&gt;At first, I assumed failures like this must be rare. Then I read the reviews.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5q90odpe0hj8lfa0vje4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5q90odpe0hj8lfa0vje4.png" alt=" " width="800" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg6bpr5beb3rtvg689r8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg6bpr5beb3rtvg689r8f.png" alt=" " width="799" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35oey8z7l371nbipnnpx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35oey8z7l371nbipnnpx.png" alt=" " width="799" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last review felt especially familiar. Keeping the entire recording until the very end and then trying to save it in one operation is, to put it mildly, risky. Until the user presses Stop, they have no way of knowing whether the recording can actually be saved.&lt;/p&gt;

&lt;p&gt;The program holds an hour of captured audio hostage and promises to hand it over only after you click Stop. If the browser crashes first, the hostage is gone. Even meeting the captor’s demands does not guarantee a happy ending: according to the reviews, the export itself can fail too.&lt;/p&gt;

&lt;p&gt;I decided not to leave users negotiating with kidnappers. Pulse saves audio locally in small chunks while recording is still in progress. If the browser closes unexpectedly, everything already written to disk remains there. Counter-Terrorists Win.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqqnkqo5sacfm4rr0sbd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqqnkqo5sacfm4rr0sbd6.png" alt=" " width="387" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A little about the architecture
&lt;/h2&gt;

&lt;p&gt;First, I had to deal with Manifest V3. Chrome may stop an extension’s background service worker whenever it considers the worker inactive. Entrusting a multi-hour recording to it would therefore be optimistic: eventually the browser might simply decide that the worker is no longer needed.&lt;/p&gt;

&lt;p&gt;In Pulse, the service worker coordinates the process. It obtains access to the tab’s audio stream, starts and stops sessions, and passes commands around. The audio itself is handled in a special hidden document that Chrome allows extensions to create for tasks like this.&lt;/p&gt;

&lt;p&gt;Chrome tab → Audio capture → Offscreen document → Encoding → IndexedDB&lt;/p&gt;

&lt;p&gt;Audio travels from the tab into the offscreen document, where it is processed and encoded in the selected format. Finished chunks are written sequentially to a local IndexedDB database. If Chrome terminates unexpectedly, the data already written to disk does not disappear with the recording process.&lt;/p&gt;

&lt;p&gt;Each chunk is stored together with its position and the current recording state. The audio data and its metadata are committed in the same transaction. This prevents Pulse from marking a chunk as saved when its write operation never actually completed.&lt;/p&gt;

&lt;p&gt;The exact process depends on the format. For WebM, the browser itself provides chunks of encoded audio roughly once per second. MP3 and WAV use a separate audio pipeline and encoder.&lt;/p&gt;

&lt;p&gt;I consider protection of already captured data a basic property of a recorder, not a premium feature for which users should thank the developer with tears in their eyes. Judging by some reviews, however, there is apparently something to be thankful for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Twenty minutes ought to be enough for anyone
&lt;/h2&gt;

&lt;p&gt;Another thing I discovered while studying competing extensions was the recording-duration limit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flbm63c8jssdd6t0ihbfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flbm63c8jssdd6t0ihbfr.png" alt=" " width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Twenty minutes. I tried to imagine a situation in which this would make sense as a universal limit. It is too long for a song, too short for a lecture, and rather absurd for a webinar. Apparently all useful knowledge stops at minute twenty-one.&lt;/p&gt;

&lt;p&gt;I did not add an artificial duration limit to Pulse. Need five minutes? Fine. Three hours? Also fine. Record as much as your computer can handle. The real limits are the available storage and the computer’s resources.&lt;/p&gt;

&lt;p&gt;If storage runs out, that is where my powers end. I can write software, but I have not yet learned to conjure extra gigabytes out of thin air. Pulse can at least give you advance warning: when Chrome estimates that less than 500 MB remains available to the extension, Pulse displays a warning and estimates how many minutes are left at the current format and quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can it do that by itself?
&lt;/h2&gt;

&lt;p&gt;I am a fairly lazy person—in the sense that I dislike performing actions a computer can handle perfectly well on its own. If I need to record a lecture, I do not want to keep checking whether it has started, whether playback has stopped, whether a long pause has begun, or whether it is time to finish the recording. The machine has a processor. Let the robots handle the repetitive work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbm5ddx5sha3m1rfbxvjf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbm5ddx5sha3m1rfbxvjf.png" alt=" " width="799" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is why Pulse includes several kinds of automation.&lt;/p&gt;

&lt;p&gt;You can delay the start by a few seconds or tell Pulse to begin only when sound appears in the tab. Recording can also stop automatically after a chosen duration, when the page changes, or after a prolonged period of silence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbx6txvhy7vfoxum4mna9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbx6txvhy7vfoxum4mna9.png" alt=" " width="386" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is also Smart Pause. It can pause a recording automatically in two ways.&lt;/p&gt;

&lt;p&gt;The first responds to silence: when sound disappears for the chosen interval, recording pauses. When sound returns, recording resumes.&lt;/p&gt;

&lt;p&gt;The second follows the media player. If a user pauses a YouTube video, Pulse pauses the recording too. When playback resumes, the recording follows immediately. Pulse does not have to wait for the silence detector’s delay because it reacts directly to the player state.&lt;/p&gt;

&lt;p&gt;Unfortunately, the same synchronization logic cannot work equally well on every website. Media players differ, and some require site-specific handling. Pulse currently supports this behavior on YouTube, VK, and Instagram. I can add other websites and social platforms if there is enough demand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disconnecting the internet, because why not?
&lt;/h2&gt;

&lt;p&gt;After I implemented automatic pausing and resuming, one beta tester decided to see what happened when the internet connection dropped. He opened a music video on YouTube, started recording, and disconnected the network. Playback stopped. Then he restored the connection, waited for the video to continue, and finished the recording.&lt;/p&gt;

&lt;p&gt;He found a problem: the join between the two audio segments was clearly audible. He reported it, and I had to investigate.&lt;/p&gt;

&lt;p&gt;Simply pausing the recorder at the same time as the player was not enough. A pause event and the actual end of audible output do not always occur at exactly the same moment. The player may already report that it has stopped while a small amount of audio still remains in the stream. If recording stops immediately, the end of a word or musical phrase gets cut off.&lt;/p&gt;

&lt;p&gt;Resuming has a similar problem. The player may already be running while the sound reaches the extension slightly later. Joining two segments at unfortunate sample positions can also create a sudden amplitude jump that the ear hears as a click.&lt;/p&gt;

&lt;p&gt;I had to work on the boundaries. For MP3 and WAV, Pulse retains a short tail of recent audio instead of cutting the stream as soon as it receives a pause event. It continues analysing the signal until the sound has actually faded. When recording resumes, Pulse checks the beginning of the next segment and, when necessary, applies a very short crossfade.&lt;/p&gt;

&lt;p&gt;Automatic silence detection created a separate issue. A detector needs a little time to notice that sound has appeared. Without compensation, the beginning of a word or musical note may be lost. Pulse therefore keeps a short pre-roll buffer of the latest input. When sound appears, that buffered audio is included so the beginning is preserved.&lt;/p&gt;

&lt;p&gt;WebM required a different approach because the audio has already been encoded and the join cannot be repaired at the sample level in the same way. I introduced a short delay into the recording pipeline and synchronized the pause and resume commands with it. This places the boundaries closer to the actual beginning and end of the sound.&lt;/p&gt;

&lt;p&gt;This took a fair amount of trial and error: change the algorithm, record test fragments, listen closely to every join, and change it again.&lt;/p&gt;

&lt;p&gt;After the fixes, the tester repeated the network-disconnection experiment. This time he could not tell where the join was. He tested it on music, where this kind of defect is especially noticeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audio quality, or why some developers add a fireplace
&lt;/h2&gt;

&lt;p&gt;The next group of reviews puzzled me. Users complained about crackling, clicks, and other unwanted sounds in their recordings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30b9exqc62jihc0zbtqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30b9exqc62jihc0zbtqw.png" alt=" " width="800" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuz9jutjsfnmln8wu1b8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuz9jutjsfnmln8wu1b8l.png" alt=" " width="800" height="90"&gt;&lt;/a&gt;&lt;br&gt;
I have a theory. Perhaps the developers wanted to make listening more comfortable: an evening, a cup of tea, favourite music, and a fireplace crackling quietly in the background. Unfortunately, their users failed to appreciate the artistic vision.&lt;/p&gt;

&lt;p&gt;I decided not to repeat that bold experiment. If someone wants the sound of a fireplace, they can open the appropriate YouTube video themselves. I spent a great deal of time on audio quality in Pulse, including the removal of unwanted clicks around pause and resume boundaries.&lt;/p&gt;

&lt;p&gt;Here is another review:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvx6ruwvhj2ivj3ju0krk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvx6ruwvhj2ivj3ju0krk.png" alt=" " width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this user, 128 kbps was not enough. The extension also stopped offering MP3 for long recordings and left WebM as the only option.&lt;/p&gt;

&lt;p&gt;Another user specifically needed WAV:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc8m0h8hroge144qfejgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc8m0h8hroge144qfejgw.png" alt=" " width="797" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pulse therefore supports three formats: MP3, WAV, and WebM. MP3 can be recorded at up to 320 kbps. WAV is available for people who need lossless PCM audio. MP3 is useful when file size and compatibility matter. WebM is one of the formats Chrome can record directly without an additional encoder.&lt;/p&gt;

&lt;p&gt;Of course, saving an already compressed source as WAV will not restore information that was previously lost. Nor will 320 kbps MP3 turn a poor source into a studio recording. At least the extension does not need to make the sound worse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78354s9d1wu7s2gzexdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78354s9d1wu7s2gzexdy.png" alt=" " width="387" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming a file so you can find it later
&lt;/h2&gt;

&lt;p&gt;Another category of requests seemed entirely reasonable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn9iab89kzk7s4anic0ax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn9iab89kzk7s4anic0ax.png" alt=" " width="800" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpcwkdvx04lhom1rkopmt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpcwkdvx04lhom1rkopmt.png" alt=" " width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A file name looks like a minor detail until you record browser audio regularly. A month later, you have several dozen files, all called something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recording_1.mp3&lt;/li&gt;
&lt;li&gt;recording_2.mp3&lt;/li&gt;
&lt;li&gt;recording_final.mp3&lt;/li&gt;
&lt;li&gt;recording_final_final.mp3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one naturally contains anything except the recording you are looking for.&lt;/p&gt;

&lt;p&gt;Pulse lets users configure file-name templates. A name can include information about the recording so that files are easy to distinguish later. This becomes particularly useful when recording several tabs at once, which brings me to the next feature.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz1qnegq6b427736nk1zc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz1qnegq6b427736nk1zc.png" alt=" " width="386" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A developer can spend a month refining an audio algorithm, and the feature that delights users most may still be sensible file names. Fortunately, one is allowed to be proud of both.&lt;/p&gt;

&lt;h2&gt;
  
  
  One tab is good. Three are better
&lt;/h2&gt;

&lt;p&gt;Some Pulse features appeared only after development had already begun. Recording several tabs simultaneously was one of them. The original workflow was ordinary: open a tab, click Record, and receive one audio file.&lt;/p&gt;

&lt;p&gt;Then I needed to capture sound from several tabs at once, so I added it. Pulse can now run multiple independent recordings in parallel. Every tab is saved separately and produces its own audio file.&lt;/p&gt;

&lt;p&gt;Under the hood, each tab gets its own recording session with an audio stream, encoder, timers, and settings. All sessions still run inside a single offscreen document because Chrome does not allow an extension to create a separate one for every tab.&lt;/p&gt;

&lt;p&gt;I also had to ensure that simultaneous commands from different tabs could not interfere with each other. If two sessions report state changes at nearly the same time, one must not overwrite the other. Pulse therefore serializes those state updates.&lt;/p&gt;

&lt;p&gt;Recording several tabs revealed another problem. If every tab plays through the speakers, working at the computer becomes difficult—especially when each speaker is talking in a different language. So I added the option to record a tab without hearing it through speakers or headphones.&lt;/p&gt;

&lt;p&gt;A Chrome behavior made this possible. When an extension captures tab audio, the browser stops routing that audio to the output device automatically. If the user should still hear it, the extension must route the captured stream back to the speakers itself.&lt;/p&gt;

&lt;p&gt;Pulse makes that routing optional. If the user wants to listen, the audio goes to the speakers or headphones. If not, monitoring stays off while recording continues. The setting does not change the quality of the saved audio.&lt;/p&gt;

&lt;p&gt;As a result, you can record a lecture in one tab and listen to music in another. No microphone is involved: Pulse receives the audio directly from the browser tab, so room conversations, keyboard noise, and other domestic sound effects do not enter the file.&lt;/p&gt;

&lt;p&gt;The interface evolved too. Extensions normally open in a small popup, which is enough for a simple action. With several recordings running, repeatedly reopening that popup becomes inconvenient. Pulse can therefore be pinned to Chrome’s side panel and remain visible while the user works.&lt;/p&gt;

&lt;h2&gt;
  
  
  An interface that does not require another degree
&lt;/h2&gt;

&lt;p&gt;Building an extension with two buttons is easy: Start and Stop. Add an attractive icon, describe the interface as “intuitive,” and declare the task complete.&lt;/p&gt;

&lt;p&gt;The opposite extreme is equally available: implement dozens of features, place every setting on one screen, and create something halfway between an aircraft cockpit and a nuclear power station control room.&lt;/p&gt;

&lt;p&gt;I did not want users to choose between simple but primitive and capable but impenetrable.&lt;/p&gt;

&lt;p&gt;My brief to the designer was therefore straightforward: a newcomer should understand how to record audio quickly, while an experienced user should still be able to find the advanced controls. I think we came reasonably close.&lt;/p&gt;

&lt;p&gt;Evaluating your own interface is difficult, of course. I helped design it, discussed every decision, and requested changes, so every button appears obvious to me.&lt;/p&gt;

&lt;p&gt;Then a user arrives and looks for an obvious feature in a place you never imagined. A tester finds a path through the interface that nobody on the team considered. If the product breaks, the interface did not protect the user well enough.&lt;/p&gt;

&lt;p&gt;The first temptation is to blame the person who could not find the button directly in front of them. I am not going to pretend that thought never occurs to me.&lt;/p&gt;

&lt;p&gt;Eventually, however, the unpleasant conclusion remains: when users do not understand an interface, the developer or designer has probably failed somewhere—especially when several users struggle in the same place.&lt;/p&gt;

&lt;p&gt;That is why feedback from people seeing Pulse for the first time is particularly valuable.&lt;/p&gt;

&lt;p&gt;The interface is currently available in nine languages. Pulse was designed for more than a Russian-speaking audience from the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why record audio from a local video?
&lt;/h2&gt;

&lt;p&gt;While reading reviews of other extensions, I found another request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpgyb58kprffnr4iwufo1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpgyb58kprffnr4iwufo1.png" alt=" " width="799" height="87"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;People sometimes need audio not only from websites but also from local files opened in the browser. Perhaps they have an MP4 video and only need its audio track. The usual solution is a separate converter, but the user can also open the video in Chrome, start playback, and record the sound with Pulse.&lt;/p&gt;

&lt;p&gt;Chrome requires the user to grant the extension access to local file URLs separately. That is a browser restriction rather than a Pulse setting.&lt;/p&gt;

&lt;p&gt;This does not replace a proper converter. Recording happens in real time, so extracting audio from a two-hour video still takes two hours. For a small clip and a one-off task, however, it works perfectly well.&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT as a voiceover tool
&lt;/h2&gt;

&lt;p&gt;Some time ago, I needed voiceovers for video tutorials about another extension I built: &lt;a href="https://formacode.dev/en/blockit" rel="noopener noreferrer"&gt;BlockIt&lt;/a&gt;, which hides or removes unwanted elements from websites. Incidentally, making those tutorials reinforced another lesson: it is better to make software understandable from the beginning than to make the developer write instructions and the user study them.&lt;/p&gt;

&lt;p&gt;Back to the voiceover. I needed the tutorials in several languages. I translated the scripts with ChatGPT and then decided to use its Read Aloud feature. ChatGPT—and DeepSeek as well—can read an assistant response aloud. I only needed a way to save the resulting audio.&lt;/p&gt;

&lt;p&gt;That is where Pulse became useful. I started Read Aloud, captured the tab audio, and received a ready-to-edit file. I then used it while assembling the tutorial videos.&lt;/p&gt;

&lt;p&gt;This allowed me to avoid a specialised paid text-to-speech service.&lt;/p&gt;

&lt;h2&gt;
  
  
  A feature I didn't even know I had
&lt;/h2&gt;

&lt;p&gt;I had planned to stop after implementing the existing features, but while collecting material for this article I found another interesting review.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuw8fcdn2a5y7zf6v7rwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuw8fcdn2a5y7zf6v7rwh.png" alt=" " width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The request concerns picture-in-picture mode, where a video continues in a small always-on-top window while the user works in another application. The reviewer could not use that mode while recording audio.&lt;/p&gt;

&lt;p&gt;Naturally, I decided to check whether Pulse had the same problem. It turned out that picture-in-picture already worked while recording. For once, I had discovered a feature that didn't require me to write another line of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free, paid, and available to bargain hunters
&lt;/h2&gt;

&lt;p&gt;I also found this review while studying competing extensions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39lft44k9sfga0nhvkxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39lft44k9sfga0nhvkxl.png" alt=" " width="482" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The frustration is understandable. Paying $30 for software that barely differs from free alternatives is hard to justify—especially if its main feature works only intermittently. At that point, even a coconut-milk latte begins to look like the more sensible investment.&lt;/p&gt;

&lt;p&gt;I chose a freemium model for Pulse. The free version remains a useful recorder in its own right, while Pro adds controls that save time and automate repetitive work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulse Free
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core recording, free and without a duration limit
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Record one tab to MP3, WAV, or WebM&lt;/li&gt;
&lt;li&gt;Quality up to 256 kbps&lt;/li&gt;
&lt;li&gt;Chunked saving while recording is in progress&lt;/li&gt;
&lt;li&gt;Delayed start using a timer&lt;/li&gt;
&lt;li&gt;Preset stop intervals: 30 minutes, 1 hour, or 2 hours&lt;/li&gt;
&lt;li&gt;Buffering time is excluded from the recording automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pulse Pro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Automation and several independent recordings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Record several tabs simultaneously&lt;/li&gt;
&lt;li&gt;Record without playing the tab through speakers or headphones&lt;/li&gt;
&lt;li&gt;Smart Pause based on silence or the media-player state&lt;/li&gt;
&lt;li&gt;Start recording when sound appears&lt;/li&gt;
&lt;li&gt;Custom stop conditions and exact durations up to 24 hours&lt;/li&gt;
&lt;li&gt;Custom file-name templates and quality up to 320 kbps&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get Pulse Pro free for life
&lt;/h2&gt;

&lt;p&gt;The first 1,000 users to activate Pulse Pro receive a &lt;strong&gt;free lifetime licence&lt;/strong&gt;, with no subscription or bank card required.&lt;/p&gt;

&lt;p&gt;To claim yours, install Pulse, click &lt;strong&gt;“Get Pulse Pro free”&lt;/strong&gt; in the extension settings, and sign in with Google. Activation takes about a minute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Installing Pulse alone does not reserve a licence. You need to complete the activation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://formacode.dev/en/pulse" rel="noopener noreferrer"&gt;Get Pulse&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Instead of a conclusion
&lt;/h2&gt;

&lt;p&gt;The result is a Chrome extension that records tab audio to MP3, WAV, or WebM without an artificial duration limit. It can capture several tabs at the same time, save each one to a separate file, and do so without forcing the user to listen to every recording.&lt;/p&gt;

&lt;p&gt;I paid particular attention to automation. Pulse can start and stop under selected conditions, skip prolonged silence, and follow playback on YouTube, VK, and Instagram. To prevent an hour-long recording from disappearing after an unexpected browser failure, Pulse writes audio to disk in small chunks while recording is still in progress.&lt;/p&gt;

&lt;p&gt;I tried to fit all of that into an interface that does not require a manual. Users will decide how successful that attempt was.&lt;/p&gt;

&lt;p&gt;Could I have built something simpler? Absolutely.&lt;/p&gt;

&lt;p&gt;Could I have spent less time developing it? Without question.&lt;/p&gt;

&lt;p&gt;But then it would have been just another extension that records audio.&lt;/p&gt;

&lt;p&gt;I wanted to build a proper one.&lt;/p&gt;

&lt;p&gt;You can explore Pulse, see its interface, and learn more about its features on the project page: &lt;a href="https://formacode.dev/en/pulse" rel="noopener noreferrer"&gt;Pulse on FormaCode.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>sideprojects</category>
      <category>tools</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Built a Chrome Audio Recorder Extension — Meet Pulse</title>
      <dc:creator>Pavel Borisov</dc:creator>
      <pubDate>Wed, 28 Oct 2026 04:10:06 +0000</pubDate>
      <link>https://dev.to/pavelcraft/i-built-a-chrome-audio-recorder-extension-meet-pulse-45a6</link>
      <guid>https://dev.to/pavelcraft/i-built-a-chrome-audio-recorder-extension-meet-pulse-45a6</guid>
      <description>&lt;p&gt;Recording audio from a browser tab sounds like a simple task. Open an extension, click Record, and save the file.&lt;/p&gt;

&lt;p&gt;But what happens when you pause a video, need to record several tabs, or your browser crashes after two hours of recording?&lt;/p&gt;

&lt;p&gt;These are some of the problems I wanted to address when I started developing &lt;strong&gt;Pulse: Free Tab Audio Recorder&lt;/strong&gt;, a Chrome extension for recording audio directly from browser tabs.&lt;/p&gt;

&lt;p&gt;Before building it, I spent time reading reviews of existing audio recording extensions in the Chrome Web Store. I was particularly interested in negative reviews and feature requests: what frustrated users, what made recording unnecessarily complicated, and what they wished their recorders could do automatically.&lt;/p&gt;

&lt;p&gt;That research helped shape Pulse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxz0s6bd41hz3kdoywrs6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxz0s6bd41hz3kdoywrs6.png" alt=" " width="387" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording Audio Shouldn't Mean Editing Out Silence Later
&lt;/h2&gt;

&lt;p&gt;Imagine you're recording a two-hour online lecture.&lt;/p&gt;

&lt;p&gt;You pause the video several times to take notes, answer a message, or grab a coffee. But your audio recorder keeps running.&lt;/p&gt;

&lt;p&gt;When you finally save the recording, it contains long stretches of silence that you now have to remove manually.&lt;/p&gt;

&lt;p&gt;I wanted Pulse to handle these situations automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start recording when audio begins
&lt;/h3&gt;

&lt;p&gt;Instead of manually coordinating the recorder with the audio player, you can configure Pulse to begin recording when sound starts playing in the tab.&lt;/p&gt;

&lt;p&gt;This is particularly useful when you're waiting for a live stream or another audio source to begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pause when playback pauses
&lt;/h3&gt;

&lt;p&gt;If you're recording a YouTube video and pause playback, Pulse can automatically pause the recording too.&lt;/p&gt;

&lt;p&gt;When playback resumes, recording continues.&lt;/p&gt;

&lt;p&gt;This avoids unnecessary silent sections without requiring you to manage two sets of controls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop when playback ends
&lt;/h3&gt;

&lt;p&gt;Pulse can also stop recording automatically when a video or audio track finishes.&lt;/p&gt;

&lt;p&gt;You don't have to keep checking whether the content has ended.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detect extended silence
&lt;/h3&gt;

&lt;p&gt;Not every silence comes from pressing a Pause button.&lt;/p&gt;

&lt;p&gt;Some streams, lectures, and recordings contain naturally occurring periods without sound. Pulse's smart pause feature can detect extended silence and pause recording automatically.&lt;/p&gt;

&lt;p&gt;These automation features help produce recordings that need less editing afterward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyjgqhs6qx5w31gyvp0o5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyjgqhs6qx5w31gyvp0o5.png" alt=" " width="387" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What If Chrome Crashes During a Long Recording?
&lt;/h2&gt;

&lt;p&gt;This was another problem I wanted to address.&lt;/p&gt;

&lt;p&gt;Imagine recording a webinar for several hours, only to have the browser close unexpectedly just before you click Stop.&lt;/p&gt;

&lt;p&gt;If the recorder depends on a final save operation, you risk losing everything.&lt;/p&gt;

&lt;p&gt;Pulse takes a different approach: &lt;strong&gt;it saves recorded audio data locally as recording progresses&lt;/strong&gt;, rather than waiting until the very end.&lt;/p&gt;

&lt;p&gt;If Chrome closes unexpectedly or the computer shuts down, the audio already captured remains available. When you reopen the extension, Pulse can finalize the interrupted recording.&lt;/p&gt;

&lt;p&gt;Of course, audio that was never captured cannot be recovered. But an unexpected shutdown shouldn't mean losing the entire recording.&lt;/p&gt;

&lt;p&gt;I also decided not to impose artificial recording time limits. Whether you're recording a short audio clip or a long lecture, Pulse doesn't stop simply because you've reached a preset duration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording Multiple Browser Tabs at Once
&lt;/h2&gt;

&lt;p&gt;Sometimes one recording isn't enough.&lt;/p&gt;

&lt;p&gt;For example, you might want to capture audio from two simultaneous live streams, or record several independent audio sources without combining them.&lt;/p&gt;

&lt;p&gt;Pulse supports recording multiple Chrome tabs simultaneously.&lt;/p&gt;

&lt;p&gt;Each tab has its own recording controls, and each recording is saved separately.&lt;/p&gt;

&lt;p&gt;You don't have to mix several audio sources into one file or run multiple instances of a recording application.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Record Audio from Chrome Without a Microphone
&lt;/h2&gt;

&lt;p&gt;Another important distinction is &lt;em&gt;where&lt;/em&gt; the audio comes from.&lt;/p&gt;

&lt;p&gt;Pulse captures audio directly from the browser tab, rather than recording sound through your microphone.&lt;/p&gt;

&lt;p&gt;That means the recording doesn't include your keyboard clicks, conversations in the room, computer fan noise, or other sounds around you.&lt;/p&gt;

&lt;p&gt;With Pulse, you can even mute the tab's audio output so you don't have to listen while recording. Pulse can continue capturing the tab's audio without playing it through your speakers or headphones.&lt;/p&gt;

&lt;p&gt;This is useful when recording a long lecture, webinar, podcast, or stream while working on something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  MP3, WAV, WebM — And Local Storage
&lt;/h2&gt;

&lt;p&gt;Different recordings call for different formats.&lt;/p&gt;

&lt;p&gt;Pulse supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MP3&lt;/strong&gt;, with bitrates up to 320 kbps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAV&lt;/strong&gt;, for uncompressed audio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebM&lt;/strong&gt;, for another browser-friendly recording option.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All recordings are stored locally on your computer. Pulse doesn't upload your audio to a remote server for processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can You Save Audio from YouTube or Instagram?
&lt;/h2&gt;

&lt;p&gt;Looking for a way to &lt;strong&gt;download music from YouTube&lt;/strong&gt;, &lt;strong&gt;save audio from Instagram videos&lt;/strong&gt;, or &lt;strong&gt;convert online videos to MP3&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Pulse offers an alternative: instead of downloading the original file, it records audio directly from your Chrome tab and saves it as MP3, WAV, or WebM.&lt;/p&gt;

&lt;p&gt;You can use it to &lt;strong&gt;record YouTube audio&lt;/strong&gt;, capture sound from Instagram clips, online radio, and other websites — without microphone noise or unnecessary silence.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Unexpected Use Case: Creating AI Voiceovers
&lt;/h2&gt;

&lt;p&gt;One way I've used Pulse myself is to create voiceovers for a video in several languages.&lt;/p&gt;

&lt;p&gt;I already had translated scripts, but I didn't want to pay for an additional text-to-speech service.&lt;/p&gt;

&lt;p&gt;So I tried a different approach.&lt;/p&gt;

&lt;p&gt;I sent the translated scripts to ChatGPT, asked it to repeat the text, and used its Read Aloud feature to generate the speech.&lt;/p&gt;

&lt;p&gt;Then I recorded the audio directly from the browser tab using Pulse.&lt;/p&gt;

&lt;p&gt;It worked. I got the voiceovers I needed without introducing another paid tool into my workflow.&lt;/p&gt;

&lt;p&gt;This is just one example. The same approach can be useful for capturing AI-generated speech, recording online lessons, saving audio from music videos, or keeping other browser audio you have permission to record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free Recording and Advanced Features
&lt;/h2&gt;

&lt;p&gt;I want Pulse to remain useful without requiring a paid license.&lt;/p&gt;

&lt;p&gt;The free version covers essential recording needs: capturing audio from one tab without time limits, choosing an output format, using basic recording controls, and recovering already recorded audio after unexpected crashes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pulse Pro&lt;/strong&gt; adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simultaneous recording from multiple tabs.&lt;/li&gt;
&lt;li&gt;Recording without playing tab audio through your speakers or headphones.&lt;/li&gt;
&lt;li&gt;Smart pause that skips long periods of silence.&lt;/li&gt;
&lt;li&gt;MP3 quality up to 320 kbps (compared to 256 kbps in the free version).&lt;/li&gt;
&lt;li&gt;Custom recording timers and automatic stopping when the page changes or silence continues.&lt;/li&gt;
&lt;li&gt;Custom filename templates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get Pulse Pro Free for Life — Limited to 1,000 Licenses
&lt;/h2&gt;

&lt;p&gt;To celebrate the launch, I'm giving away &lt;strong&gt;1,000 free lifetime Pulse Pro licenses&lt;/strong&gt; to the first users who activate them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important: Installing Pulse does not automatically activate your Pro license.&lt;/strong&gt; It's a separate step that takes less than a minute:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the extension's settings and go to the &lt;strong&gt;License&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;Follow the link to the license activation page.&lt;/li&gt;
&lt;li&gt;Sign in with your Google account.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Activate&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F56bonrurzeseybpd8ncu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F56bonrurzeseybpd8ncu.png" alt=" " width="385" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! Your lifetime Pro license is activated, with all Pro features unlocked and no subscription or future payments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The offer is limited to the first 1,000 activations, so don't forget to claim your license while it's still available.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Pulse and Share Your Feedback
&lt;/h2&gt;

&lt;p&gt;Pulse is an independent project that I'm continuing to develop.&lt;/p&gt;

&lt;p&gt;Many of its features came from reading what users were asking for in reviews of other browser audio recorders. I plan to keep listening to that feedback as I improve the extension.&lt;/p&gt;

&lt;p&gt;If you regularly record webinars, online lectures, podcasts, live streams, or AI-generated speech, I'd be interested to know which recording features matter most to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://formacode.dev/en/pulse" rel="noopener noreferrer"&gt;Try Pulse and activate your free lifetime Pro license&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feedback, bug reports, and feature suggestions are welcome.&lt;/p&gt;

</description>
      <category>chrome</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>BlockIt: Flexible Web Element Blocking with Full Control Over Rules</title>
      <dc:creator>Pavel Borisov</dc:creator>
      <pubDate>Fri, 23 Oct 2026 01:27:33 +0000</pubDate>
      <link>https://dev.to/pavelcraft/blockit-flexible-web-element-blocking-with-full-control-over-rules-350i</link>
      <guid>https://dev.to/pavelcraft/blockit-flexible-web-element-blocking-with-full-control-over-rules-350i</guid>
      <description>&lt;p&gt;Many websites contain elements you'd rather not see: intrusive banners, pop-ups, advertisements, or simply unnecessary parts of the interface.&lt;/p&gt;

&lt;p&gt;Modern ad blockers not only use predefined filter lists but also allow users to manually select elements to block. In most cases, that's enough: select an element, create a rule, and it no longer gets in your way.&lt;/p&gt;

&lt;p&gt;But sometimes things are more complicated.&lt;/p&gt;

&lt;p&gt;For example, a blocked element may reappear after a page reload because its classes or position in the DOM have changed. Or an automatically generated rule may affect other elements that the user never intended to block.&lt;/p&gt;

&lt;p&gt;In these situations, it's important not only to specify &lt;strong&gt;which element should be blocked, but also to determine exactly which properties should be used to find it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To address these challenges, I developed &lt;strong&gt;BlockIt&lt;/strong&gt; — a free Chrome extension that lets users create flexible rules for blocking web page elements.&lt;/p&gt;

&lt;p&gt;BlockIt supports standard CSS selectors, XPath, and its own extended syntax, which can take additional properties of elements and their surroundings into account: text, attributes, classes, DOM position, relationships with other elements, and much more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can also create and customize rules without knowing selector syntax or specialized expressions.&lt;/strong&gt; BlockIt provides tools for choosing relevant element properties, excluding unstable attributes and classes, combining search conditions, and checking whether a rule continues to work after a page reload.&lt;/p&gt;

&lt;p&gt;This makes BlockIt useful both for advanced users and web developers who need detailed control over element matching, and for anyone who has encountered a particularly annoying element that cannot be reliably removed using conventional methods.&lt;/p&gt;

&lt;p&gt;The main idea behind BlockIt is to &lt;strong&gt;give users the tools to create precise, reliable rules while letting them decide which properties should be used to identify elements for blocking&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Regular CSS Selector Isn't Always Enough
&lt;/h2&gt;

&lt;p&gt;Imagine a page containing several similar blocks. You need to block just one of them without affecting the others.&lt;/p&gt;

&lt;p&gt;Chrome DevTools lets you automatically copy the CSS selector of a selected element. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#app&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This selector finds a &lt;code&gt;div&lt;/code&gt; element that is the third child of another &lt;code&gt;div&lt;/code&gt;. That parent &lt;code&gt;div&lt;/code&gt;, in turn, must be the second child of the element with the ID &lt;code&gt;app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The selector may work correctly on the current page. But if the website's developers add another container or change the order of elements, it may start matching a different block or stop working entirely.&lt;/p&gt;

&lt;p&gt;Another common problem is dynamically generated classes.&lt;/p&gt;

&lt;p&gt;For example, an element might look like this on one page load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_a8f32"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And like this on the next:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_c91e4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selector &lt;code&gt;.banner_a8f32&lt;/code&gt; matches an element with the class &lt;code&gt;banner_a8f32&lt;/code&gt;. Once the class changes, it can no longer find the intended block.&lt;/p&gt;

&lt;p&gt;Sometimes choosing a more stable CSS selector is enough. But there are situations where precise matching requires additional properties: text content, the number of attributes, position relative to other elements, or a combination of several conditions.&lt;/p&gt;

&lt;p&gt;BlockIt provides additional capabilities for exactly these situations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a BlockIt Rule?
&lt;/h2&gt;

&lt;p&gt;Before exploring the different ways to create rules, it's worth clarifying the terminology.&lt;/p&gt;

&lt;p&gt;In the broad sense, a &lt;strong&gt;BlockIt rule&lt;/strong&gt; is a description of how to find page elements that should be blocked.&lt;/p&gt;

&lt;p&gt;Several methods can be used to find elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSS selector&lt;/strong&gt; — the standard syntax for selecting DOM elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XPath&lt;/strong&gt; — an expression language for locating elements within a document's structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BlockIt syntax&lt;/strong&gt; — the extension's own syntax, which adds capabilities beyond standard CSS selectors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, even an ordinary CSS selector becomes a BlockIt blocking rule when added to the extension.&lt;/p&gt;

&lt;p&gt;However, the extension's interface also uses the term &lt;strong&gt;"BlockIt Rule" in a narrower sense&lt;/strong&gt; — to refer specifically to an expression written using BlockIt's own syntax, as opposed to a standard CSS selector.&lt;/p&gt;

&lt;p&gt;For example, after creating a rule, you may see a label such as "CSS Selector" or "BlockIt Rule". In this context, these labels indicate the syntax being used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Several Ways to Create a Rule
&lt;/h2&gt;

&lt;p&gt;I wanted BlockIt to be suitable for both simple tasks and situations that require detailed control over element matching.&lt;/p&gt;

&lt;p&gt;That's why the extension offers several ways to create rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. CSS Selector
&lt;/h3&gt;

&lt;p&gt;The simplest approach is to select an element in Chrome DevTools, copy its CSS selector, and paste it into BlockIt.&lt;/p&gt;

&lt;p&gt;But DevTools isn't required: &lt;strong&gt;you can write a CSS selector manually&lt;/strong&gt; or edit an existing one.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.notice&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"promo"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This selector matches &lt;code&gt;div&lt;/code&gt; elements that have the class &lt;code&gt;notice&lt;/code&gt; and a &lt;code&gt;data-type&lt;/code&gt; attribute with the value &lt;code&gt;promo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The extension displays the number of matching elements. This helps you check whether the rule affects any unintended elements.&lt;/p&gt;

&lt;p&gt;You can choose one of two actions for matched elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remove&lt;/strong&gt; — completely remove the element from the DOM, usually freeing up the space it occupied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hide&lt;/strong&gt; — make the element invisible while preserving the space it occupies on the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second option is useful when removing an element disrupts the page layout.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. XPath
&lt;/h3&gt;

&lt;p&gt;BlockIt also supports XPath expressions.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="ow"&gt;div&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;@data-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'promo'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This expression finds &lt;code&gt;div&lt;/code&gt; elements whose &lt;code&gt;data-type&lt;/code&gt; attribute has the value &lt;code&gt;promo&lt;/code&gt;, regardless of their position in the document.&lt;/p&gt;

&lt;p&gt;XPath provides additional ways to locate elements and navigate the DOM.&lt;/p&gt;

&lt;p&gt;However, XPath does not guarantee stability by itself. If an expression relies on changing attributes or element positions, it can also stop working.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Quick Rule from HTML
&lt;/h3&gt;

&lt;p&gt;One of the most convenient ways to create rules is the &lt;strong&gt;Quick Rule&lt;/strong&gt; tool.&lt;/p&gt;

&lt;p&gt;Instead of providing a selector, you can simply copy the HTML of the desired element from Chrome DevTools and paste it into the corresponding field in BlockIt.&lt;/p&gt;

&lt;p&gt;The extension parses the HTML and lets you choose which properties should be used for matching.&lt;/p&gt;

&lt;p&gt;For example, suppose the original element looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cmpbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cmpbox_a8f32"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the class &lt;code&gt;cmpbox_a8f32&lt;/code&gt; is dynamically generated, you can exclude it and keep only the attributes &lt;code&gt;id="cmpbox"&lt;/code&gt; and &lt;code&gt;role="dialog"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then simply click the "Add Rule" button.&lt;/p&gt;

&lt;p&gt;This way, users can decide which attributes and classes to include without having to write a selector manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv6xfalk11gv7li07fif.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv6xfalk11gv7li07fif.png" alt="Quick Rule tool in BlockIt" width="438" height="905"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule Builder
&lt;/h2&gt;

&lt;p&gt;For more complex cases, BlockIt includes a &lt;strong&gt;Rule Builder&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It allows you to define element-matching conditions through a graphical interface without requiring knowledge of CSS, XPath, or BlockIt's own syntax.&lt;/p&gt;

&lt;p&gt;The Rule Builder can take into account not only an element's own properties but also its surroundings.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML tag, classes, and attributes;&lt;/li&gt;
&lt;li&gt;text content, including substring matching and regular expressions;&lt;/li&gt;
&lt;li&gt;number of classes and attributes;&lt;/li&gt;
&lt;li&gt;position among sibling elements;&lt;/li&gt;
&lt;li&gt;properties of parents, ancestors, descendants, and siblings;&lt;/li&gt;
&lt;li&gt;distance to a sibling and the absence of certain related elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These conditions can be combined.&lt;/p&gt;

&lt;p&gt;Suppose a page contains several elements with identical classes, but the one you need is inside a container with a particular attribute. Instead of relying on the element's position, you can use properties of its surrounding elements.&lt;/p&gt;

&lt;p&gt;Or, if classes keep changing, you can try identifying the element by its text and other more stable characteristics.&lt;/p&gt;

&lt;p&gt;The Rule Builder displays a human-readable description of the generated rule. This helps users understand which properties are involved in matching, even if they aren't familiar with selector syntax.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv3mwfglhl5ibiuot97i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv3mwfglhl5ibiuot97i.png" alt="BlockIt Rule Builder" width="800" height="659"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing After a Page Reload
&lt;/h3&gt;

&lt;p&gt;Finding the correct element once doesn't necessarily mean you've created a reliable rule.&lt;/p&gt;

&lt;p&gt;On dynamic websites, it's important to make sure the rule continues to work on subsequent page loads.&lt;/p&gt;

&lt;p&gt;That's why the Rule Builder includes a &lt;strong&gt;test after page reload&lt;/strong&gt; feature.&lt;/p&gt;

&lt;p&gt;It lets you compare the number of matching elements before and after reloading the page.&lt;/p&gt;

&lt;p&gt;If matches disappear or their number changes, this may indicate that the selected conditions are unstable.&lt;/p&gt;

&lt;p&gt;You can adjust the rule and run the test again.&lt;/p&gt;

&lt;p&gt;Of course, matching the same number of elements doesn't guarantee that the rule will always find exactly what you intended. But this test helps identify certain problems while the rule is still being created.&lt;/p&gt;

&lt;h2&gt;
  
  
  BlockIt's Extended Syntax
&lt;/h2&gt;

&lt;p&gt;Not all conditions available in the Rule Builder can be expressed using standard CSS selectors.&lt;/p&gt;

&lt;p&gt;For example, standard CSS does not support matching elements by arbitrary text content or checking the number of attributes an element has.&lt;/p&gt;

&lt;p&gt;For these situations, I developed BlockIt's own syntax, which extends the capabilities of CSS selectors.&lt;/p&gt;

&lt;p&gt;For example, you can match elements by their own text while ignoring text inside nested elements, check the number of classes and attributes, or define additional conditions involving surrounding elements.&lt;/p&gt;

&lt;p&gt;If the conditions selected in the Rule Builder can be expressed using standard CSS, the result will be an ordinary CSS selector.&lt;/p&gt;

&lt;p&gt;If additional capabilities are required, the Rule Builder generates a &lt;strong&gt;"BlockIt Rule"&lt;/strong&gt; — here, the term is used in its narrower sense, meaning an expression written using the extended syntax.&lt;/p&gt;

&lt;p&gt;In either case, the user gets a ready-to-use rule for finding and blocking elements.&lt;/p&gt;

&lt;p&gt;And there's no need to learn BlockIt's syntax: complex conditions can be configured directly in the Rule Builder.&lt;/p&gt;

&lt;p&gt;For those who prefer writing expressions manually, the &lt;a href="https://pavelcraft.github.io/BlockIt/rules/en/index.html" rel="noopener noreferrer"&gt;BlockIt syntax documentation&lt;/a&gt; is also available.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a Regular Element-Blocking Tool Isn't Enough
&lt;/h2&gt;

&lt;p&gt;Imagine a situation: an annoying element appears on a website, and the standard filters of your installed ad blocker don't remove it.&lt;/p&gt;

&lt;p&gt;You decide to block it manually using the ad blocker's built-in element picker. You select the unwanted block, create a rule, and the element disappears.&lt;/p&gt;

&lt;p&gt;But after reloading the page, it comes back.&lt;/p&gt;

&lt;p&gt;The reason may be that the automatically generated selector relied on a dynamically generated class or a changing DOM structure.&lt;/p&gt;

&lt;p&gt;In this situation, BlockIt offers several possible approaches.&lt;/p&gt;

&lt;p&gt;You can manually choose a more stable CSS selector, use XPath, or create a more complex rule with additional conditions through the Rule Builder.&lt;/p&gt;

&lt;p&gt;For example, you can stop relying on a dynamic class and instead use the element's text, attributes, or properties of surrounding containers.&lt;/p&gt;

&lt;p&gt;You can then test the rule in the Rule Builder after reloading the page.&lt;/p&gt;

&lt;p&gt;This can help block certain complex elements for which an automatically suggested selector wasn't reliable enough.&lt;/p&gt;

&lt;p&gt;Of course, similar problems can be solved using other tools. For example, uBlock Origin supports advanced cosmetic filters that can express complex matching conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes BlockIt distinctive is its combination of multiple ways to create rules, detailed control over matching conditions, and tools that make complex rules accessible without requiring users to learn specialized syntax.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the same time, the extension doesn't promise to automatically find the perfect properties for every element.&lt;/p&gt;

&lt;p&gt;A rule's reliability depends on the characteristics of the particular page and how well its matching conditions are chosen. BlockIt provides the tools to tackle this problem, but the final choice remains with the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with iframes and Shadow DOM
&lt;/h2&gt;

&lt;p&gt;Some page elements are not located in the main DOM document.&lt;/p&gt;

&lt;p&gt;For example, content may be placed inside an &lt;code&gt;iframe&lt;/code&gt; or use Shadow DOM.&lt;/p&gt;

&lt;p&gt;This introduces additional challenges when finding and blocking elements.&lt;/p&gt;

&lt;p&gt;BlockIt supports applying rules inside iframes and Shadow DOM, although different matching methods have certain technical limitations.&lt;/p&gt;

&lt;p&gt;In particular, XPath cannot cross a Shadow DOM boundary. For such cases, rules based on CSS or BlockIt's extended syntax can be used.&lt;/p&gt;

&lt;p&gt;In addition, BlockIt's own syntax provides special conditions related to iframes and Shadow DOM.&lt;/p&gt;

&lt;p&gt;This expands the possibilities for finding elements in complex web page structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  How BlockIt Relates to StableHTMLFinder
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/pavelcraft/why-css-selectors-break-and-how-i-built-stablehtmlfinder-to-analyze-dom-stability-30n2"&gt;previous article about StableHTMLFinder&lt;/a&gt;, I discussed the problem of unstable CSS selectors and how to analyze DOM changes across page loads.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder and BlockIt solve different but related problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StableHTMLFinder&lt;/strong&gt; helps identify which elements and properties remain stable across repeated page loads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BlockIt&lt;/strong&gt; lets you use those properties to find and block unwanted elements.&lt;/p&gt;

&lt;p&gt;For example, if a block's classes keep changing, you can first analyze the page using StableHTMLFinder, identify stable properties of the element itself or its surroundings, and then use them to create a rule in BlockIt.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder can also generate CSS selectors and expressions using BlockIt's syntax, which you can copy into the blocking extension.&lt;/p&gt;

&lt;p&gt;However, using both tools together is entirely optional: BlockIt works independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing Rules
&lt;/h2&gt;

&lt;p&gt;Once you've created a rule, you can also manage it.&lt;/p&gt;

&lt;p&gt;BlockIt lets you disable rules without deleting them, organizes them by website, and supports exporting and importing settings.&lt;/p&gt;

&lt;p&gt;This is useful when you've accumulated many rules for different websites.&lt;/p&gt;

&lt;p&gt;All rules are stored locally in the browser and are not sent to a server.&lt;/p&gt;

&lt;p&gt;BlockIt's source code is open and available on GitHub under the BSD-3-Clause license.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try BlockIt
&lt;/h2&gt;

&lt;p&gt;BlockIt is a free Chrome extension.&lt;/p&gt;

&lt;p&gt;If you'd like to try it yourself, visit the &lt;a href="https://formacode.dev/en/blockit" rel="noopener noreferrer"&gt;&lt;strong&gt;official BlockIt page&lt;/strong&gt;&lt;/a&gt;, where you'll find a link to install the extension from the Chrome Web Store.&lt;/p&gt;

&lt;p&gt;I've also prepared a &lt;a href="https://pavelcraft.github.io/BlockIt/en/instruction.html" rel="noopener noreferrer"&gt;&lt;strong&gt;detailed guide with examples and screenshots&lt;/strong&gt;&lt;/a&gt; to help you explore the extension's features. It covers different ways to create rules, from a simple CSS selector to the Rule Builder with complex conditions.&lt;/p&gt;

&lt;p&gt;If you've encountered elements that were difficult to block using ordinary CSS selectors, I'd be interested to hear how you approached the problem.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>chrome</category>
      <category>opensource</category>
    </item>
    <item>
      <title>BlockIt: гибкая блокировка элементов веб-страниц с полным контролем над правилами</title>
      <dc:creator>Pavel Borisov</dc:creator>
      <pubDate>Thu, 22 Oct 2026 19:44:23 +0000</pubDate>
      <link>https://dev.to/pavelcraft/blockit-ghibkaia-blokirovka-eliemientov-vieb-stranits-s-polnym-kontroliem-nad-pravilami-96n</link>
      <guid>https://dev.to/pavelcraft/blockit-ghibkaia-blokirovka-eliemientov-vieb-stranits-s-polnym-kontroliem-nad-pravilami-96n</guid>
      <description>&lt;p&gt;На многих сайтах встречаются элементы, которые хотелось бы убрать: навязчивые баннеры, всплывающие окна, рекламные блоки или просто ненужные части интерфейса.&lt;/p&gt;

&lt;p&gt;Современные блокировщики рекламы позволяют не только использовать готовые списки фильтрации, но и самостоятельно выбирать элементы для блокировки. В большинстве случаев этого достаточно: выбрал элемент, создал правило — и он больше не мешает.&lt;/p&gt;

&lt;p&gt;Но иногда всё оказывается сложнее.&lt;/p&gt;

&lt;p&gt;Например, после обновления страницы заблокированный элемент появляется снова, потому что его классы или положение в DOM изменились. Или автоматически созданное правило затрагивает другие элементы, которые пользователь вовсе не собирался блокировать.&lt;/p&gt;

&lt;p&gt;В таких ситуациях важно не просто указать, &lt;strong&gt;какой элемент нужно заблокировать, но и определить, по каким именно признакам его следует находить&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Для решения подобных задач я разработал &lt;strong&gt;BlockIt&lt;/strong&gt; — бесплатное расширение для Chrome, позволяющее создавать гибкие правила блокировки элементов веб-страниц.&lt;/p&gt;

&lt;p&gt;В BlockIt можно использовать обычные CSS-селекторы, XPath или собственный расширенный синтаксис, который позволяет учитывать дополнительные характеристики элементов и их окружения: текст, атрибуты, классы, положение в DOM, связи с другими элементами и многое другое.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;При этом создавать и настраивать правила можно без знания синтаксиса селекторов или специальных выражений.&lt;/strong&gt; BlockIt предоставляет инструменты, позволяющие выбирать нужные признаки элементов, исключать нестабильные атрибуты и классы, комбинировать условия поиска и проверять, продолжает ли правило работать после обновления страницы.&lt;/p&gt;

&lt;p&gt;Таким образом, BlockIt может быть полезен как опытным пользователям и веб-разработчикам, которым нужен подробный контроль над поиском элементов, так и тем, кто просто столкнулся с особенно назойливым блоком, который не удаётся надёжно убрать обычным способом.&lt;/p&gt;

&lt;p&gt;Главная идея BlockIt — &lt;strong&gt;дать пользователю инструменты для создания точных и устойчивых правил, сохранив возможность самостоятельно решать, какие признаки использовать для блокировки&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Почему обычного CSS-селектора иногда недостаточно
&lt;/h2&gt;

&lt;p&gt;Представим, что на странице есть несколько похожих блоков. Нужно заблокировать только один из них, не затрагивая остальные.&lt;/p&gt;

&lt;p&gt;Chrome DevTools позволяет автоматически скопировать CSS-селектор выбранного элемента. Например:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#app&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Этот селектор ищет элемент div, который является третьим дочерним элементом другого div. Тот, в свою очередь, должен быть вторым дочерним элементом элемента с идентификатором app.&lt;/p&gt;

&lt;p&gt;На текущей странице он может работать правильно. Но если разработчики добавят новый контейнер или изменят порядок элементов, селектор может начать находить другой блок либо вообще перестать работать.&lt;/p&gt;

&lt;p&gt;Другая распространённая проблема — динамически генерируемые классы.&lt;/p&gt;

&lt;p&gt;Например, при одной загрузке страницы элемент выглядит так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_a8f32"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;А при следующей:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_c91e4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Селектор &lt;code&gt;.banner_a8f32&lt;/code&gt; ищет элемент с классом &lt;code&gt;banner_a8f32&lt;/code&gt;. После изменения класса он больше не найдёт нужный блок.&lt;/p&gt;

&lt;p&gt;Иногда достаточно подобрать более устойчивый CSS-селектор. Но бывают ситуации, когда для точного поиска необходимо учитывать дополнительные признаки: текстовое содержимое, количество атрибутов, положение относительно других элементов или сочетание нескольких условий.&lt;/p&gt;

&lt;p&gt;Именно для таких случаев в BlockIt предусмотрены дополнительные возможности.&lt;/p&gt;

&lt;h2&gt;
  
  
  Что такое правило BlockIt
&lt;/h2&gt;

&lt;p&gt;Прежде чем перейти к способам создания правил, стоит разобраться с терминологией.&lt;/p&gt;

&lt;p&gt;В широком смысле &lt;strong&gt;правило BlockIt&lt;/strong&gt; — это описание того, как найти элементы страницы, которые необходимо заблокировать.&lt;/p&gt;

&lt;p&gt;Для поиска можно использовать:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSS-селектор&lt;/strong&gt; — стандартный синтаксис для поиска элементов DOM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XPath&lt;/strong&gt; — язык выражений для поиска элементов в структуре документа.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Синтаксис BlockIt&lt;/strong&gt; — собственный синтаксис расширения, дополняющий возможности CSS-селекторов.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Таким образом, даже обычный CSS-селектор, добавленный в расширение, становится правилом блокировки BlockIt.&lt;/p&gt;

&lt;p&gt;Однако в интерфейсе расширения термин &lt;strong&gt;«Правило BlockIt» используется и в более узком смысле&lt;/strong&gt; — для обозначения выражения, написанного с использованием собственного синтаксиса BlockIt, в отличие от обычного CSS-селектора.&lt;/p&gt;

&lt;p&gt;Например, после создания правила можно увидеть пометку «CSS-селектор» или «Правило BlockIt». В этом случае названия указывают именно на используемый синтаксис.&lt;/p&gt;

&lt;h2&gt;
  
  
  Несколько способов создать правило
&lt;/h2&gt;

&lt;p&gt;Я хотел, чтобы BlockIt подходил как для простых задач, так и для ситуаций, когда требуется подробный контроль над поиском элементов.&lt;/p&gt;

&lt;p&gt;Поэтому в расширении предусмотрено несколько способов создания правил.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. CSS-селектор
&lt;/h3&gt;

&lt;p&gt;Самый простой вариант — выбрать элемент в Chrome DevTools, скопировать его CSS-селектор и вставить в BlockIt.&lt;/p&gt;

&lt;p&gt;Но использовать DevTools необязательно: &lt;strong&gt;CSS-селектор можно написать вручную&lt;/strong&gt; или отредактировать уже существующий.&lt;/p&gt;

&lt;p&gt;Например:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.notice&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"promo"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Этот селектор найдёт элементы &lt;code&gt;div&lt;/code&gt;, у которых есть класс &lt;code&gt;notice&lt;/code&gt; и атрибут &lt;code&gt;data-type&lt;/code&gt; со значением &lt;code&gt;promo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Расширение показывает количество найденных совпадений. Это помогает проверить, не затрагивает ли правило лишние элементы.&lt;/p&gt;

&lt;p&gt;Для найденных элементов можно выбрать одно из двух действий:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Удалить&lt;/strong&gt; — полностью убрать элемент из DOM, обычно освободив занимаемое им место.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Скрыть&lt;/strong&gt; — сделать элемент невидимым, сохранив занимаемое им место на странице.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Второй вариант полезен, если после удаления элемента нарушается вёрстка.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. XPath
&lt;/h3&gt;

&lt;p&gt;BlockIt также поддерживает XPath-выражения.&lt;/p&gt;

&lt;p&gt;Например:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="ow"&gt;div&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;@data-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'promo'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Это выражение ищет элементы &lt;code&gt;div&lt;/code&gt;, у которых атрибут &lt;code&gt;data-type&lt;/code&gt; имеет значение &lt;code&gt;promo&lt;/code&gt;, независимо от их расположения в документе.&lt;/p&gt;

&lt;p&gt;XPath предоставляет дополнительные способы поиска элементов и навигации по DOM.&lt;/p&gt;

&lt;p&gt;Однако сам по себе XPath не гарантирует устойчивости. Если выражение опирается на изменяющиеся атрибуты или положение элементов, оно тоже может перестать работать.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Быстрое правило из HTML
&lt;/h3&gt;

&lt;p&gt;Один из наиболее удобных способов создания правил — &lt;strong&gt;«Быстрое правило»&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Вместо готового селектора достаточно скопировать HTML нужного элемента из Chrome DevTools и вставить его в соответствующее поле BlockIt.&lt;/p&gt;

&lt;p&gt;Расширение разбирает HTML и позволяет выбрать, какие признаки следует использовать при поиске.&lt;/p&gt;

&lt;p&gt;Например, исходный элемент выглядит так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cmpbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cmpbox_a8f32"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Если класс cmpbox_a8f32 генерируется динамически, его можно исключить, оставив только атрибуты id="cmpbox" и role="dialog".&lt;/p&gt;

&lt;p&gt;После этого достаточно нажать кнопку «Добавить правило».&lt;/p&gt;

&lt;p&gt;Таким образом, пользователь самостоятельно определяет, какие атрибуты и классы учитывать, но ему не нужно писать селектор вручную.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv6xfalk11gv7li07fif.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv6xfalk11gv7li07fif.png" alt=" " width="438" height="905"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Конструктор правила
&lt;/h2&gt;

&lt;p&gt;Для более сложных случаев в BlockIt предусмотрен &lt;strong&gt;конструктор правила&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Он позволяет описывать условия поиска элементов через интерфейс, не требуя знания CSS, XPath или собственного синтаксиса BlockIt.&lt;/p&gt;

&lt;p&gt;В конструкторе можно учитывать не только признаки самого элемента, но и его окружение.&lt;/p&gt;

&lt;p&gt;Например:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML-тег, классы и атрибуты;&lt;/li&gt;
&lt;li&gt;текстовое содержимое, включая поиск по части текста и регулярным выражениям;&lt;/li&gt;
&lt;li&gt;количество классов и атрибутов;&lt;/li&gt;
&lt;li&gt;положение элемента среди соседних элементов;&lt;/li&gt;
&lt;li&gt;признаки родителей, предков, потомков и сиблингов;&lt;/li&gt;
&lt;li&gt;расстояние до сиблинга и отсутствие определённых связанных элементов.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Условия можно комбинировать.&lt;/p&gt;

&lt;p&gt;Допустим, на странице несколько элементов с одинаковыми классами, но нужный находится внутри контейнера с определённым атрибутом. Вместо того чтобы полагаться на порядковый номер элемента, можно использовать признаки его окружения.&lt;/p&gt;

&lt;p&gt;Или, если классы постоянно меняются, попробовать найти элемент по тексту и другим более устойчивым характеристикам.&lt;/p&gt;

&lt;p&gt;Конструктор показывает словесное описание сформированного правила. Это помогает контролировать, какие именно признаки участвуют в поиске, даже если пользователь не знаком с синтаксисом селекторов.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv3mwfglhl5ibiuot97i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv3mwfglhl5ibiuot97i.png" alt=" " width="800" height="659"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Проверка после обновления страницы
&lt;/h3&gt;

&lt;p&gt;Найти нужный элемент один раз — ещё не значит создать надёжное правило.&lt;/p&gt;

&lt;p&gt;На динамических сайтах важно убедиться, что оно продолжает работать при последующих загрузках.&lt;/p&gt;

&lt;p&gt;Поэтому в конструкторе правила предусмотрена &lt;strong&gt;проверка после обновления страницы&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Она позволяет сравнить количество найденных элементов до и после перезагрузки.&lt;/p&gt;

&lt;p&gt;Если совпадения исчезли или их количество изменилось, это может указывать на нестабильность выбранных условий.&lt;/p&gt;

&lt;p&gt;Можно скорректировать правило и повторить проверку.&lt;/p&gt;

&lt;p&gt;Разумеется, совпадение количества элементов ещё не гарантирует, что правило всегда будет находить именно то, что нужно. Но такая проверка помогает обнаружить некоторые проблемы ещё на этапе создания правила.&lt;/p&gt;

&lt;h2&gt;
  
  
  Расширенный синтаксис BlockIt
&lt;/h2&gt;

&lt;p&gt;Не все условия, доступные в конструкторе, можно выразить обычным CSS-селектором.&lt;/p&gt;

&lt;p&gt;Например, стандартный CSS не предусматривает поиск по произвольному текстовому содержимому или проверку количества атрибутов элемента.&lt;/p&gt;

&lt;p&gt;Для таких случаев я разработал собственный синтаксис BlockIt, расширяющий возможности CSS-селекторов.&lt;/p&gt;

&lt;p&gt;Например, можно искать элементы по собственному тексту, не учитывая текст вложенных элементов, проверять количество классов и атрибутов или задавать дополнительные условия для окружающих элементов.&lt;/p&gt;

&lt;p&gt;Если выбранные в конструкторе условия можно выразить стандартным CSS, результатом будет обычный CSS-селектор.&lt;/p&gt;

&lt;p&gt;Если требуются дополнительные возможности, конструктор сформирует &lt;strong&gt;«Правило BlockIt»&lt;/strong&gt; — здесь этот термин используется в узком смысле, то есть обозначает выражение с использованием расширенного синтаксиса.&lt;/p&gt;

&lt;p&gt;В обоих случаях пользователь получает готовое правило для поиска и блокировки элементов.&lt;/p&gt;

&lt;p&gt;При этом изучать синтаксис BlockIt необязательно: сложные условия можно составлять непосредственно в конструкторе.&lt;/p&gt;

&lt;p&gt;А для тех, кто предпочитает работать с выражениями вручную, доступна &lt;a href="https://pavelcraft.github.io/BlockIt/rules/ru/index.html" rel="noopener noreferrer"&gt;документация синтаксиса BlockIt&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Когда обычный инструмент блокировки элементов не справляется
&lt;/h2&gt;

&lt;p&gt;Представим ситуацию: на сайте появился раздражающий блок, который не блокируется стандартными фильтрами установленного блокировщика рекламы.&lt;/p&gt;

&lt;p&gt;Пользователь решает заблокировать его вручную с помощью встроенного инструмента выбора элементов. Он выделяет нужный блок, создаёт правило — и элемент исчезает.&lt;/p&gt;

&lt;p&gt;Но после обновления страницы он появляется снова.&lt;/p&gt;

&lt;p&gt;Причина может заключаться в том, что автоматически сформированный селектор опирался на динамический класс или изменяющуюся структуру DOM.&lt;/p&gt;

&lt;p&gt;В такой ситуации BlockIt предлагает несколько вариантов решения.&lt;/p&gt;

&lt;p&gt;Можно самостоятельно подобрать более устойчивый CSS-селектор, воспользоваться XPath или составить сложное правило с дополнительными условиями через конструктор.&lt;/p&gt;

&lt;p&gt;Например, отказаться от динамического класса и учитывать текст элемента, его атрибуты или признаки окружающих контейнеров.&lt;/p&gt;

&lt;p&gt;После этого можно проверить работу правила в конструкторе при повторной загрузке страницы.&lt;/p&gt;

&lt;p&gt;Это позволяет справляться с некоторыми сложными элементами, для которых автоматически предложенный селектор оказался недостаточно надёжным.&lt;/p&gt;

&lt;p&gt;Конечно, подобные задачи можно решать и другими инструментами. В частности, uBlock Origin поддерживает расширенные косметические фильтры, позволяющие описывать сложные условия поиска.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Особенность BlockIt — в сочетании нескольких способов создания правил, подробного контроля над условиями поиска и инструментов, позволяющих работать со сложными условиями без изучения специального синтаксиса.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;При этом расширение не обещает автоматически находить идеальные признаки для любого элемента.&lt;/p&gt;

&lt;p&gt;Надёжность правила зависит от особенностей конкретной страницы и от того, насколько удачно выбраны условия поиска. BlockIt предоставляет инструменты для решения этой задачи, но окончательный выбор остаётся за пользователем.&lt;/p&gt;

&lt;h2&gt;
  
  
  Работа с iframe и Shadow DOM
&lt;/h2&gt;

&lt;p&gt;Некоторые элементы страницы находятся не в основном DOM-документе.&lt;/p&gt;

&lt;p&gt;Например, содержимое может размещаться внутри &lt;code&gt;iframe&lt;/code&gt; или использовать Shadow DOM.&lt;/p&gt;

&lt;p&gt;Это создаёт дополнительные сложности при поиске и блокировке элементов.&lt;/p&gt;

&lt;p&gt;BlockIt поддерживает применение правил внутри iframe и Shadow DOM, хотя для разных способов поиска существуют технические ограничения.&lt;/p&gt;

&lt;p&gt;В частности, XPath не может пересекать границу Shadow DOM. Для таких случаев можно использовать правила на основе CSS или расширенного синтаксиса BlockIt.&lt;/p&gt;

&lt;p&gt;Кроме того, собственный синтаксис BlockIt предусматривает специальные условия, связанные с iframe и Shadow DOM.&lt;/p&gt;

&lt;p&gt;Это расширяет возможности поиска элементов в сложных структурах веб-страниц.&lt;/p&gt;

&lt;h2&gt;
  
  
  Как BlockIt связан со StableHTMLFinder
&lt;/h2&gt;

&lt;p&gt;В &lt;a href="https://dev.to/pavelcraft/pochiemu-css-sieliektory-lomaiutsia-i-kak-ia-sozdal-stablehtmlfinder-dlia-analiza-stabilnosti-dom-4ga1"&gt;предыдущей статье о StableHTMLFinder&lt;/a&gt; я рассказывал о проблеме нестабильных CSS-селекторов и о том, как анализировать изменения DOM между загрузками страницы.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder и BlockIt решают разные, но связанные задачи.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StableHTMLFinder&lt;/strong&gt; помогает определить, какие элементы и признаки сохраняются при повторных загрузках страницы.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BlockIt&lt;/strong&gt; позволяет использовать выбранные признаки для поиска и блокировки нежелательных элементов.&lt;/p&gt;

&lt;p&gt;Например, если классы нужного блока постоянно меняются, можно сначала исследовать страницу в StableHTMLFinder, найти устойчивые признаки самого элемента или его окружения, а затем использовать их для создания правила в BlockIt.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder также может формировать CSS-селекторы и правила с использованием синтаксиса BlockIt, которые можно скопировать в расширение для блокировки.&lt;/p&gt;

&lt;p&gt;Но использовать оба инструмента вместе необязательно: BlockIt работает самостоятельно.&lt;/p&gt;

&lt;h2&gt;
  
  
  Управление правилами
&lt;/h2&gt;

&lt;p&gt;После создания правила можно не только применять, но и управлять им.&lt;/p&gt;

&lt;p&gt;BlockIt позволяет отключать правила без удаления, группирует их по сайтам и поддерживает экспорт и импорт настроек.&lt;/p&gt;

&lt;p&gt;Это удобно, если со временем накапливается много правил для разных страниц.&lt;/p&gt;

&lt;p&gt;Все правила хранятся локально в браузере и не отправляются на сервер.&lt;/p&gt;

&lt;p&gt;Исходный код BlockIt открыт и доступен на GitHub под лицензией BSD-3-Clause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Попробуйте BlockIt
&lt;/h2&gt;

&lt;p&gt;BlockIt — бесплатное расширение для Chrome.&lt;/p&gt;

&lt;p&gt;Если хотите попробовать его самостоятельно, посетите &lt;a href="https://formacode.dev/ru/blockit" rel="noopener noreferrer"&gt;&lt;strong&gt;официальную страницу BlockIt&lt;/strong&gt;&lt;/a&gt;, там вы найдёте ссылку для скачивания расширения из Chrome Web Store.&lt;/p&gt;

&lt;p&gt;Для знакомства с возможностями расширения я подготовил &lt;a href="https://pavelcraft.github.io/BlockIt/ru/instruction.html" rel="noopener noreferrer"&gt;&lt;strong&gt;подробную инструкцию с примерами и скриншотами&lt;/strong&gt;&lt;/a&gt;. В ней рассмотрены разные способы создания правил — от простого CSS-селектора до конструктора со сложными условиями.&lt;/p&gt;

&lt;p&gt;Если вы сталкивались с элементами, которые сложно заблокировать обычными CSS-селекторами, мне было бы интересно узнать, какие способы вы использовали для решения этой задачи.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>chrome</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Почему CSS-селекторы ломаются и как я создал StableHTMLFinder для анализа стабильности DOM</title>
      <dc:creator>Pavel Borisov</dc:creator>
      <pubDate>Thu, 22 Oct 2026 15:34:47 +0000</pubDate>
      <link>https://dev.to/pavelcraft/pochiemu-css-sieliektory-lomaiutsia-i-kak-ia-sozdal-stablehtmlfinder-dlia-analiza-stabilnosti-dom-4ga1</link>
      <guid>https://dev.to/pavelcraft/pochiemu-css-sieliektory-lomaiutsia-i-kak-ia-sozdal-stablehtmlfinder-dlia-analiza-stabilnosti-dom-4ga1</guid>
      <description>&lt;p&gt;Если вы когда-нибудь писали CSS-селекторы для автоматизации браузера, парсинга страниц или блокировки нежелательных элементов, то наверняка сталкивались с ситуацией: вчера селектор работал, а сегодня перестал находить нужный элемент.&lt;/p&gt;

&lt;p&gt;Причина часто заключается не в ошибке селектора, а в самой странице. Современные сайты активно используют динамически генерируемые классы, изменяющиеся атрибуты, рекламные блоки и другие элементы, структура которых может отличаться при каждой загрузке.&lt;/p&gt;

&lt;p&gt;Я столкнулся с этой проблемой при разработке &lt;strong&gt;BlockIt&lt;/strong&gt; — расширения для Chrome, позволяющего блокировать нежелательные элементы на веб-страницах.&lt;/p&gt;

&lt;p&gt;В какой-то момент мне понадобился инструмент, который помог бы понять, какие части DOM действительно остаются неизменными при повторных загрузках страницы.&lt;/p&gt;

&lt;p&gt;Так появился &lt;strong&gt;StableHTMLFinder&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;В этой статье я расскажу, почему решил его создать, что представляет собой карта стабильности DOM и как она помогает находить устойчивые признаки элементов, не углубляясь во внутреннюю реализацию алгоритмов.&lt;/p&gt;

&lt;h2&gt;
  
  
  Проблема: DOM постоянно меняется
&lt;/h2&gt;

&lt;p&gt;Представим, что нам нужно скрыть рекламный блок на сайте.&lt;/p&gt;

&lt;p&gt;Открываем DevTools, выбираем элемент и видим примерно такой HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ad-container_x7Kp2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_9fA31"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Рекламное объявление
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Можно написать селектор:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.ad-container_x7Kp2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Он работает. Но после обновления страницы HTML уже выглядит иначе:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ad-container_m4Rz8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_2qB76"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Рекламное объявление
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Селектор перестаёт находить элемент.&lt;/p&gt;

&lt;p&gt;Конечно, можно попробовать использовать частичное совпадение класса:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;class&lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="s1"&gt;"ad-container_"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Но такой подход работает лишь тогда, когда изменяемые значения сохраняют предсказуемую структуру.&lt;/p&gt;

&lt;p&gt;А если меняются не только классы, но и идентификаторы, атрибуты, количество вложенных элементов или расположение блока?&lt;/p&gt;

&lt;p&gt;Приходится изучать окружающую структуру страницы и искать более надёжные ориентиры.&lt;/p&gt;

&lt;p&gt;Именно здесь возникает главная проблема: &lt;strong&gt;как отличить действительно стабильные признаки DOM от тех, которые лишь случайно выглядят стабильными при текущей загрузке?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Почему одного снимка DOM недостаточно
&lt;/h2&gt;

&lt;p&gt;Большинство привычных инструментов генерации селекторов анализируют текущее состояние страницы.&lt;/p&gt;

&lt;p&gt;Они могут определить уникальный класс, идентификатор, построить CSS-путь или XPath.&lt;/p&gt;

&lt;p&gt;Однако уникальность и стабильность — совершенно разные свойства.&lt;/p&gt;

&lt;p&gt;Например:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"action_583921"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn_8xQ2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Продолжить
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Идентификатор &lt;code&gt;action_583921&lt;/code&gt; может быть уникальным на странице. Но это ничего не говорит о том, сохранится ли он после обновления.&lt;/p&gt;

&lt;p&gt;Инструмент, который видит только один снимок DOM, не может непосредственно наблюдать изменение этого идентификатора между загрузками.&lt;/p&gt;

&lt;p&gt;Конечно, существуют эвристики, позволяющие предположить, что определённый класс или идентификатор генерируется динамически. Но предположение не заменяет наблюдение.&lt;/p&gt;

&lt;p&gt;Когда я начинал разрабатывать StableHTMLFinder в 2026 году, мне не удалось найти инструмент, который автоматически собирал бы несколько снимков одной страницы и на их основе строил наглядную карту устойчивых элементов и их признаков.&lt;/p&gt;

&lt;p&gt;При этом я не хотел создавать ещё один генератор селекторов.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Первоначальная идея была гораздо проще: убрать из DOM весь нестабильный информационный шум и посмотреть, что останется.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Карта стабильности DOM
&lt;/h2&gt;

&lt;p&gt;StableHTMLFinder — бесплатное расширение для Chrome, которое анализирует несколько последовательных загрузок веб-страницы и строит на их основе &lt;strong&gt;карту стабильности&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;По своей структуре она напоминает DOM-дерево, но имеет принципиальное отличие: в ней отображаются только те элементы и признаки, стабильность которых удалось установить.&lt;/p&gt;

&lt;p&gt;Допустим, при нескольких загрузках страницы встречается один и тот же элемент:&lt;/p&gt;

&lt;p&gt;Первая загрузка:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content_a72"&lt;/span&gt; &lt;span class="na"&gt;data-session=&lt;/span&gt;&lt;span class="s"&gt;"1842"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Новости&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Вторая загрузка:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content_b91"&lt;/span&gt; &lt;span class="na"&gt;data-session=&lt;/span&gt;&lt;span class="s"&gt;"7501"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Новости&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Третья загрузка:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content_c38"&lt;/span&gt; &lt;span class="na"&gt;data-session=&lt;/span&gt;&lt;span class="s"&gt;"3926"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Новости&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В этом упрощённом примере можно заметить:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Тег &lt;code&gt;section&lt;/code&gt; сохраняется.&lt;/li&gt;
&lt;li&gt;Атрибут &lt;code&gt;class&lt;/code&gt; присутствует, но его значение меняется.&lt;/li&gt;
&lt;li&gt;Атрибут &lt;code&gt;data-session&lt;/code&gt; присутствует, но его значение тоже меняется.&lt;/li&gt;
&lt;li&gt;Дочерний элемент &lt;code&gt;h2&lt;/code&gt; и его класс остаются неизменными.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Если сопоставление элементов между загрузками достаточно надёжно, карта может представить эту структуру примерно так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt; &lt;span class="na"&gt;data-session&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Новости
    &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Это схематический пример, а не точное представление интерфейса StableHTMLFinder.&lt;/p&gt;

&lt;p&gt;Обратите внимание: имена атрибутов &lt;code&gt;class&lt;/code&gt; и &lt;code&gt;data-session&lt;/code&gt; остались, а их нестабильные значения исчезли.&lt;/p&gt;

&lt;p&gt;Таким образом, карта позволяет различать несколько ситуаций:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Стабилен элемент вместе с атрибутами и их значениями.&lt;/li&gt;
&lt;li&gt;Стабилен элемент и наличие определённых атрибутов, но не их значения.&lt;/li&gt;
&lt;li&gt;Удалось установить стабильность самого элемента, но его атрибуты не дают надёжных ориентиров.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Смысл такого представления — не показать пользователю как можно больше HTML, а оставить только информацию, которая потенциально полезна для построения устойчивого селектора.&lt;/p&gt;

&lt;p&gt;Вместо того чтобы разбираться в десятках случайных классов, можно сосредоточиться на признаках, которые действительно сохраняются между загрузками.&lt;/p&gt;

&lt;p&gt;На скриншоте показан фрагмент карты стабильности DOM, построенной расширением StableHTMLFinder. Зелёным цветом выделены стабильные элементы и их атрибуты, сохраняющиеся при повторных загрузках страницы. Один из элементов, ссылка &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, раскрыт для просмотра дополнительной информации. Для него StableHTMLFinder сформировал CSS-селектор и правило для расширения BlockIt, которые можно скопировать одним нажатием. Древовидная структура карты позволяет исследовать вложенные элементы и их атрибуты, а также переходить от анализа стабильности DOM к практическому использованию найденных селекторов.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhkxmvyfophquras8d7pd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhkxmvyfophquras8d7pd.png" alt=" " width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Полностью и относительно стабильные элементы
&lt;/h2&gt;

&lt;p&gt;Не все элементы удаётся сопоставить между загрузками с одинаковой уверенностью.&lt;/p&gt;

&lt;p&gt;Поэтому StableHTMLFinder различает полностью и относительно стабильные элементы.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Полностью стабильные элементы&lt;/strong&gt; — наиболее надёжные участки карты. Их собственные признаки, а также соответствующие признаки родительских и непосредственно дочерних элементов совпадают между наблюдениями.&lt;/p&gt;

&lt;p&gt;Такие элементы служат хорошими ориентирами при анализе структуры страницы.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Относительно стабильные элементы&lt;/strong&gt; — элементы, для которых имеются достаточные основания считать, что в разных загрузках речь идёт об одном и том же DOM-узле, хотя часть признаков или окружающей структуры изменяется.&lt;/p&gt;

&lt;p&gt;На карте они выделяются более светлым цветом.&lt;/p&gt;

&lt;p&gt;Это важное различие: StableHTMLFinder не утверждает, что всё на странице либо абсолютно неизменно, либо полностью непригодно для анализа.&lt;/p&gt;

&lt;p&gt;В реальных веб-приложениях ситуация гораздо сложнее.&lt;/p&gt;

&lt;p&gt;Элемент может сохранять своё назначение, но менять классы. Его родитель может получать новые атрибуты. Между двумя знакомыми элементами могут появляться дополнительные контейнеры.&lt;/p&gt;

&lt;p&gt;Поэтому полезно видеть не только абсолютно стабильные участки, но и те, для которых удалось установить относительную стабильность.&lt;/p&gt;

&lt;h2&gt;
  
  
  Островки стабильности внутри динамического DOM
&lt;/h2&gt;

&lt;p&gt;Одна из наиболее интересных особенностей StableHTMLFinder — способность отображать &lt;strong&gt;островки стабильности&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Представим, что между загрузками страницы меняется целая цепочка вложенных контейнеров.&lt;/p&gt;

&lt;p&gt;В одном случае нужный элемент находится внутри двух промежуточных блоков, в другом — внутри четырёх. Их классы и атрибуты также могут различаться.&lt;/p&gt;

&lt;p&gt;Но где-то внутри этой нестабильной структуры сохраняется элемент с узнаваемыми признаками.&lt;/p&gt;

&lt;p&gt;Было бы неправильно полностью отбрасывать его только потому, что его предки нестабильны.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder сохраняет такие стабильные участки на карте, а разделяющую их нестабильную структуру обозначает специальным серым элементом &amp;lt;...&amp;gt;.&lt;/p&gt;

&lt;p&gt;Они выглядят как элементы дерева, но фактически обозначают изменяющуюся структуру произвольной вложенности. Этот элемент может представлять произвольное количество нестабильных уровней вложенности, а не только один пропущенный узел.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv2aglzwyuogbqbe8o4t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv2aglzwyuogbqbe8o4t.png" alt=" " width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;В результате пользователь видит, что между стабильными участками страницы существует нестабильная область, но при этом не теряет возможность исследовать расположенные внутри неё элементы.&lt;/p&gt;

&lt;p&gt;Это особенно полезно на страницах, где JavaScript динамически перестраивает отдельные части интерфейса.&lt;/p&gt;

&lt;h2&gt;
  
  
  Поиск элементов на карте стабильности
&lt;/h2&gt;

&lt;p&gt;Допустим, вы обнаружили на сайте элемент, который хотите заблокировать.&lt;/p&gt;

&lt;p&gt;Открыли DevTools, нашли соответствующий HTML, но его классы и идентификаторы выглядят случайными.&lt;/p&gt;

&lt;p&gt;Вместо ручного исследования множества родительских и дочерних узлов можно воспользоваться поиском StableHTMLFinder.&lt;/p&gt;

&lt;p&gt;Он позволяет искать элементы на карте несколькими способами, в том числе:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Вставить HTML-элемент, скопированный из DevTools.&lt;/li&gt;
&lt;li&gt;Указать селектор для поиска нужного элемента.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Если элемент присутствует на карте стабильности, можно перейти к его карточке и изучить доступные устойчивые признаки.&lt;/p&gt;

&lt;p&gt;Это позволяет быстро перейти от исходного DOM, заполненного динамическими значениями, к его очищенному представлению.&lt;/p&gt;

&lt;p&gt;Особенно удобно, когда проблемный элемент сам по себе не содержит подходящих признаков, но рядом с ним находятся более стабильные элементы.&lt;/p&gt;

&lt;h3&gt;
  
  
  Как повысить точность поиска
&lt;/h3&gt;

&lt;p&gt;StableHTMLFinder выполняет поиск преимущественно по последнему снимку DOM, полученному во время анализа страницы.&lt;/p&gt;

&lt;p&gt;Если после завершения анализа страница не перезагружалась, а нужный элемент ещё не изменился под воздействием JavaScript, его можно скопировать из DevTools и вставить в поиск. В этом случае расширение точно найдёт соответствующий элемент на карте стабильности, даже если все его атрибуты и их значения оказались нестабильными и на карте сохранился только тег элемента.&lt;/p&gt;

&lt;p&gt;Если же вы скопировали элемент, который был уже изменён после последнего снимка DOM, поиск всё ещё может оказаться успешным. StableHTMLFinder попытается сопоставить его с элементами карты по сохранившимся признакам, однако уверенность в результате будет ниже.&lt;/p&gt;

&lt;h2&gt;
  
  
  Автоматическая генерация селекторов
&lt;/h2&gt;

&lt;p&gt;Хотя StableHTMLFinder изначально задумывался как инструмент визуального анализа DOM, впоследствии я добавил автоматическую генерацию селекторов.&lt;/p&gt;

&lt;p&gt;Это оказалось естественным продолжением основной идеи.&lt;/p&gt;

&lt;p&gt;Если инструмент уже определил стабильные признаки элемента и его окружения, почему бы не попробовать автоматически построить селектор на их основе?&lt;/p&gt;

&lt;p&gt;В карточке элемента можно получить предложенный селектор и скопировать его.&lt;/p&gt;

&lt;p&gt;Рядом отображается текстовое объяснение, позволяющее понять, на каких признаках он основан.&lt;/p&gt;

&lt;p&gt;Это важно, потому что селектор не должен оставаться для пользователя непонятной строкой.&lt;/p&gt;

&lt;p&gt;Однако здесь есть &lt;strong&gt;важное ограничение&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;В особо сложных случаях StableHTMLFinder может оказаться не в состоянии сформировать уникальный селектор для определённого элемента.&lt;/p&gt;

&lt;p&gt;Хотя расширение обычно успешно формирует уникальные селекторы, у некоторых элементов отсутствует уникальная комбинация стабильных признаков в их ближнем окружении DOM. В таких редких случаях StableHTMLFinder явно сообщает, что сформировать уникальный селектор не удалось.&lt;/p&gt;

&lt;p&gt;В таких случаях сама карта стабильности всё равно может быть полезна: она предоставляет информацию для самостоятельного поиска решения.&lt;/p&gt;

&lt;p&gt;Именно поэтому я рассматриваю генерацию селекторов как вспомогательную возможность, а не как основное назначение расширения.&lt;/p&gt;

&lt;h2&gt;
  
  
  Как StableHTMLFinder связан с BlockIt
&lt;/h2&gt;

&lt;p&gt;StableHTMLFinder появился в процессе работы над другим моим проектом — &lt;strong&gt;BlockIt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;BlockIt — расширение Chrome для блокировки нежелательных элементов на веб-страницах.&lt;/p&gt;

&lt;p&gt;Оно позволяет создавать правила с использованием CSS, XPath и дополнительных условий, а также применять более сложные способы поиска элементов.&lt;/p&gt;

&lt;p&gt;Но даже достаточно выразительный синтаксис не решает главную проблему: если правило основано на нестабильных признаках, оно может перестать работать после очередного обновления страницы.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder помогает найти более надёжные ориентиры для таких правил.&lt;/p&gt;

&lt;p&gt;При этом два расширения решают разные задачи:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StableHTMLFinder&lt;/strong&gt; анализирует страницу, показывает карту стабильности и помогает создавать селекторы. Само расширение ничего не блокирует.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BlockIt&lt;/strong&gt; использует правила для поиска и блокировки нежелательных элементов на страницах.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder также может предлагать специальные правила, предназначенные для использования в BlockIt.&lt;/p&gt;

&lt;p&gt;Таким образом, одно расширение помогает исследовать DOM и подготовить правило, а другое — применить его на практике.&lt;/p&gt;

&lt;p&gt;Подробнее о BlockIt я расскажу в отдельной статье.&lt;/p&gt;

&lt;h2&gt;
  
  
  Может ли StableHTMLFinder быть полезен за пределами блокировки рекламы?
&lt;/h2&gt;

&lt;p&gt;Хотя проект возник из задачи блокировки нежелательных элементов, сама проблема нестабильных DOM-локаторов встречается и в других областях.&lt;/p&gt;

&lt;p&gt;Например, при разработке браузерной автоматизации и UI-тестов.&lt;/p&gt;

&lt;p&gt;Если тест использует динамический класс, он может перестать работать после изменения страницы, даже когда нужная кнопка или форма по-прежнему существует.&lt;/p&gt;

&lt;p&gt;Карта стабильности потенциально может помочь разработчику определить более надёжные ориентиры.&lt;/p&gt;

&lt;p&gt;Аналогичная задача иногда возникает при создании скриптов, взаимодействующих с веб-страницами, и инструментов извлечения данных.&lt;/p&gt;

&lt;p&gt;Однако StableHTMLFinder не является универсальным решением для этих сценариев. Его возможности зависят от структуры конкретного сайта и наличия признаков, которые сохраняются между загрузками.&lt;/p&gt;

&lt;p&gt;Кроме того, стабильность, установленная на основании нескольких наблюдений, не означает, что элемент никогда не изменится в будущем.&lt;/p&gt;

&lt;p&gt;Карта отражает результаты проведённого анализа, а не даёт абсолютную гарантию.&lt;/p&gt;

&lt;h2&gt;
  
  
  Почему я считаю этот подход полезным
&lt;/h2&gt;

&lt;p&gt;При разработке StableHTMLFinder я хотел изменить сам процесс работы с DOM.&lt;/p&gt;

&lt;p&gt;Обычно разработчик открывает DevTools, видит огромное дерево элементов и пытается самостоятельно определить, какие признаки пригодны для селектора.&lt;/p&gt;

&lt;p&gt;Но большая часть этой информации может оказаться бесполезной: случайные классы, временные идентификаторы, динамические атрибуты, постоянно перестраивающиеся контейнеры.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder предлагает другой подход:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Сначала определить, что остаётся стабильным между загрузками страницы, и только потом решать, как использовать эти признаки.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Это не устраняет все сложности динамического DOM и не делает селекторы неуязвимыми к изменениям сайта.&lt;/p&gt;

&lt;p&gt;Но позволяет сосредоточиться на более надёжной информации и избежать значительной части ручной работы.&lt;/p&gt;

&lt;p&gt;И, пожалуй, это лучше всего описывает первоначальную идею проекта: не столько автоматически написать селектор за пользователя, сколько дать ему возможность увидеть страницу без нестабильного информационного шума.&lt;/p&gt;




&lt;h2&gt;
  
  
  Попробуйте StableHTMLFinder
&lt;/h2&gt;

&lt;p&gt;StableHTMLFinder — бесплатное расширение для Chrome.&lt;/p&gt;

&lt;p&gt;Если хотите попробовать его в работе, посетите &lt;a href="https://formacode.dev/ru/stablehtmlfinder" rel="noopener noreferrer"&gt;официальную страницу проекта StableHTMLFinder&lt;/a&gt;, где вы найдёте ссылку для установки расширения из Chrome Web Store.&lt;/p&gt;

&lt;p&gt;Если вы работаете с динамическими веб-страницами, мне было бы интересно узнать, с какими проблемами стабильности селекторов вы сталкивались и как подходили к их решению.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>css</category>
      <category>dom</category>
    </item>
    <item>
      <title>Why CSS Selectors Break — and How I Built StableHTMLFinder to Analyze DOM Stability</title>
      <dc:creator>Pavel Borisov</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/pavelcraft/why-css-selectors-break-and-how-i-built-stablehtmlfinder-to-analyze-dom-stability-30n2</link>
      <guid>https://dev.to/pavelcraft/why-css-selectors-break-and-how-i-built-stablehtmlfinder-to-analyze-dom-stability-30n2</guid>
      <description>&lt;p&gt;If you've ever written CSS selectors for browser automation, web scraping, or blocking unwanted elements, you've probably encountered this situation: a selector works perfectly today, but tomorrow it can no longer find the element you need.&lt;/p&gt;

&lt;p&gt;Often, the problem isn't the selector itself. It's the page.&lt;/p&gt;

&lt;p&gt;Modern websites frequently use dynamically generated classes, changing attributes, advertising elements, and JavaScript-driven interfaces whose DOM structure can differ between page loads.&lt;/p&gt;

&lt;p&gt;I encountered this problem while developing &lt;strong&gt;BlockIt&lt;/strong&gt;, a Chrome extension for blocking unwanted elements on web pages.&lt;/p&gt;

&lt;p&gt;At some point, I needed a tool that would help me understand which parts of the DOM actually remain unchanged across repeated page loads.&lt;/p&gt;

&lt;p&gt;That's how &lt;strong&gt;StableHTMLFinder&lt;/strong&gt; was born.&lt;/p&gt;

&lt;p&gt;In this article, I'll explain why I created it, what a DOM stability map is, and how it helps identify reliable element characteristics — without going into the internal implementation of its algorithms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: The DOM Keeps Changing
&lt;/h2&gt;

&lt;p&gt;Imagine you want to hide an advertising block on a website.&lt;/p&gt;

&lt;p&gt;You open DevTools, inspect the element, and see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ad-container_x7Kp2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_9fA31"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Advertisement
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could write a selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.ad-container_x7Kp2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works. But after refreshing the page, the HTML looks different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ad-container_m4Rz8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner_2qB76"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Advertisement
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your selector no longer matches the element.&lt;/p&gt;

&lt;p&gt;Of course, you could try matching part of the class name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;class&lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="s1"&gt;"ad-container_"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this approach only works when dynamically generated values follow a predictable pattern.&lt;/p&gt;

&lt;p&gt;What if not only classes but also IDs, attributes, nesting levels, or element positions change?&lt;/p&gt;

&lt;p&gt;You have to examine the surrounding DOM structure and look for more reliable reference points.&lt;/p&gt;

&lt;p&gt;This leads to the central question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can we distinguish genuinely stable DOM characteristics from those that merely happen to look stable in the current page load?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Single DOM Snapshot Isn't Enough
&lt;/h2&gt;

&lt;p&gt;Many conventional selector-generation tools analyze the current state of a page.&lt;/p&gt;

&lt;p&gt;They can identify a unique class or ID, construct a CSS path, or generate an XPath expression.&lt;/p&gt;

&lt;p&gt;But uniqueness and stability are two very different things.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"action_583921"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn_8xQ2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Continue
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ID &lt;code&gt;action_583921&lt;/code&gt; might be unique on the page. But that tells us nothing about whether it will remain the same after a refresh.&lt;/p&gt;

&lt;p&gt;A tool that examines only one DOM snapshot cannot directly observe how that ID changes between page loads.&lt;/p&gt;

&lt;p&gt;Of course, heuristics can help identify classes or IDs that appear to be dynamically generated. But an educated guess is not the same as observation.&lt;/p&gt;

&lt;p&gt;When I started developing StableHTMLFinder in 2026, I couldn't find a tool that automatically collected multiple snapshots of the same page and used them to build a visual map of stable elements and their characteristics.&lt;/p&gt;

&lt;p&gt;And I wasn't trying to build another selector generator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My original idea was much simpler: remove all the unstable noise from the DOM and see what remains.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the DOM Stability Map
&lt;/h2&gt;

&lt;p&gt;StableHTMLFinder is a free Chrome extension that analyzes multiple consecutive page loads and builds a &lt;strong&gt;stability map&lt;/strong&gt; from the collected DOM snapshots.&lt;/p&gt;

&lt;p&gt;Its structure resembles a DOM tree, but with one fundamental difference: it displays only elements and characteristics whose stability has been established during the analysis.&lt;/p&gt;

&lt;p&gt;Suppose the same element appears across several page loads.&lt;/p&gt;

&lt;p&gt;First load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content_a72"&lt;/span&gt; &lt;span class="na"&gt;data-session=&lt;/span&gt;&lt;span class="s"&gt;"1842"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;News&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content_b91"&lt;/span&gt; &lt;span class="na"&gt;data-session=&lt;/span&gt;&lt;span class="s"&gt;"7501"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;News&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content_c38"&lt;/span&gt; &lt;span class="na"&gt;data-session=&lt;/span&gt;&lt;span class="s"&gt;"3926"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;News&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this simplified example, we can observe that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;section&lt;/code&gt; tag remains unchanged.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;class&lt;/code&gt; attribute is always present, but its value changes.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;data-session&lt;/code&gt; attribute is always present, but its value also changes.&lt;/li&gt;
&lt;li&gt;The child &lt;code&gt;h2&lt;/code&gt; element and its class remain unchanged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the elements can be matched reliably across page loads, the stability map might represent the structure approximately like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt; &lt;span class="na"&gt;data-session&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        News
    &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This is a schematic illustration, not an exact representation of the StableHTMLFinder interface.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Notice that the attribute names &lt;code&gt;class&lt;/code&gt; and &lt;code&gt;data-session&lt;/code&gt; remain, while their unstable values disappear.&lt;/p&gt;

&lt;p&gt;The stability map can therefore distinguish between several situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An element, its attributes, and their values are stable.&lt;/li&gt;
&lt;li&gt;An element and certain attribute names are stable, but their values are not.&lt;/li&gt;
&lt;li&gt;The element itself can be identified across page loads, but its attributes provide no reliable reference points.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The purpose of this representation isn't to show as much HTML as possible.&lt;/p&gt;

&lt;p&gt;It's to retain only information that may be useful for constructing a reliable selector.&lt;/p&gt;

&lt;p&gt;Instead of examining dozens of random-looking classes, you can focus on characteristics that actually remain consistent across page loads.&lt;/p&gt;

&lt;p&gt;Here's what a section of the stability map looks like in practice. This example shows a fully stable part of a web page, where elements and their attributes remain consistent across multiple page loads. The tree-like structure makes it easy to explore these elements and their relationships without the distraction of unstable DOM characteristics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhegy4haulb9pjj6dcgf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhegy4haulb9pjj6dcgf5.png" alt=" " width="800" height="663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fully Stable and Relatively Stable Elements
&lt;/h2&gt;

&lt;p&gt;Not every element can be matched across page loads with the same level of confidence.&lt;/p&gt;

&lt;p&gt;That's why StableHTMLFinder distinguishes between fully stable and relatively stable elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fully stable elements&lt;/strong&gt; are the most reliable parts of the stability map. Their own characteristics, along with those of their parents and immediate children, match exactly across the analyzed snapshots.&lt;/p&gt;

&lt;p&gt;These elements provide particularly useful reference points when examining the page structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relatively stable elements&lt;/strong&gt; are elements for which there is sufficient evidence that they represent the same DOM element across different page loads, even though some characteristics or parts of the surrounding structure may change.&lt;/p&gt;

&lt;p&gt;They are displayed in a lighter color on the stability map.&lt;/p&gt;

&lt;p&gt;This is an important distinction: StableHTMLFinder doesn't assume that everything on a page must be either completely stable or entirely unsuitable for analysis.&lt;/p&gt;

&lt;p&gt;In real-world web applications, things are much more complicated.&lt;/p&gt;

&lt;p&gt;An element may retain its purpose while its classes change. Its parent may receive new attributes. Additional containers may appear between otherwise familiar elements.&lt;/p&gt;

&lt;p&gt;Rather than discarding everything that isn't perfectly stable, StableHTMLFinder preserves elements whose identity can still be established with reasonable confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Islands of Stability Inside a Dynamic DOM
&lt;/h2&gt;

&lt;p&gt;One of the more interesting features of StableHTMLFinder is its ability to display &lt;strong&gt;islands of stability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine that an entire chain of nested containers changes between page loads.&lt;/p&gt;

&lt;p&gt;During one load, the element you're interested in is nested inside two intermediate containers. During another, it might be nested inside four. Their classes and attributes may also be different.&lt;/p&gt;

&lt;p&gt;But somewhere inside this unstable structure, an element with recognizable characteristics remains.&lt;/p&gt;

&lt;p&gt;It would be a mistake to discard that element simply because its ancestors are unstable.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder can preserve such an element on the stability map while representing the unstable intermediate levels using special gray placeholders.&lt;/p&gt;

&lt;p&gt;These placeholders resemble elements in the tree, but they actually represent unstable structures with potentially varying nesting depths.&lt;/p&gt;

&lt;p&gt;As a result, you can see that an unstable region exists between stable parts of the page without losing the ability to examine the elements inside it.&lt;/p&gt;

&lt;p&gt;This can be particularly useful on websites where JavaScript dynamically rebuilds parts of the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Elements on the Stability Map
&lt;/h2&gt;

&lt;p&gt;Suppose you've found an element on a website that you want to block.&lt;/p&gt;

&lt;p&gt;You open DevTools and inspect its HTML, but the classes and IDs look randomly generated.&lt;/p&gt;

&lt;p&gt;Instead of manually examining numerous parent and child elements, you can use StableHTMLFinder's search feature.&lt;/p&gt;

&lt;p&gt;The search supports several approaches, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pasting an HTML element copied from DevTools.&lt;/li&gt;
&lt;li&gt;Entering a selector to locate the corresponding element.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the element exists on the stability map, you can navigate to its card and examine the available stable characteristics.&lt;/p&gt;

&lt;p&gt;This provides a convenient way to move from the original DOM, filled with dynamically changing values, to a cleaner representation of the same page.&lt;/p&gt;

&lt;p&gt;It's particularly helpful when the target element itself has no reliable attributes, but more stable elements exist nearby.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Improve Search Accuracy
&lt;/h3&gt;

&lt;p&gt;StableHTMLFinder performs its search primarily against the last DOM snapshot collected during the analysis.&lt;/p&gt;

&lt;p&gt;If you haven't reloaded the page since the analysis finished, and the target element hasn't changed due to JavaScript activity, you can copy it from DevTools and paste it into the search field.&lt;/p&gt;

&lt;p&gt;In this case, the extension will accurately locate the corresponding element on the stability map, even if all its attributes and their values turned out to be unstable and only the element's tag remains on the map.&lt;/p&gt;

&lt;p&gt;If, however, you copied an element that had already changed since the last DOM snapshot, the search may still succeed. StableHTMLFinder will attempt to match it using the characteristics that remain recognizable, but with a lower level of confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic Selector Generation
&lt;/h2&gt;

&lt;p&gt;Although StableHTMLFinder was originally designed as a visual DOM analysis tool, I later added automatic selector generation.&lt;/p&gt;

&lt;p&gt;It seemed like a natural extension of the original idea.&lt;/p&gt;

&lt;p&gt;If the tool has already identified stable characteristics of an element and its surroundings, why not try generating a selector from them?&lt;/p&gt;

&lt;p&gt;From an element's card, you can obtain a suggested selector and copy it.&lt;/p&gt;

&lt;p&gt;A human-readable explanation is displayed alongside the selector, describing which characteristics were used to construct it.&lt;/p&gt;

&lt;p&gt;I consider this important because a selector shouldn't be an incomprehensible string that users have to trust blindly.&lt;/p&gt;

&lt;p&gt;However, there's an important limitation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In particularly complex cases, StableHTMLFinder may be unable to generate a unique selector for a given element.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Although the extension can usually generate a unique selector, some elements lack a unique combination of stable characteristics within their nearby DOM structure. In these rare cases, StableHTMLFinder clearly indicates that a unique selector could not be generated.&lt;/p&gt;

&lt;p&gt;Some pages are structured in ways that simply don't provide enough stable characteristics.&lt;/p&gt;

&lt;p&gt;In those cases, the stability map can still be valuable because it gives you the information needed to investigate the problem and develop your own solution.&lt;/p&gt;

&lt;p&gt;That's why I see automatic selector generation as a convenience feature rather than the extension's primary purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  How StableHTMLFinder Connects to BlockIt
&lt;/h2&gt;

&lt;p&gt;StableHTMLFinder originated while I was working on another project of mine: &lt;strong&gt;BlockIt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;BlockIt is a Chrome extension for blocking unwanted elements on web pages.&lt;/p&gt;

&lt;p&gt;It supports rules based on CSS, XPath, and additional conditions, along with more advanced methods of locating elements.&lt;/p&gt;

&lt;p&gt;But even a powerful rule syntax can't solve the fundamental problem: if a rule relies on unstable characteristics, it may stop working after the next page load.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder helps identify more reliable reference points for those rules.&lt;/p&gt;

&lt;p&gt;The two extensions serve different purposes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StableHTMLFinder&lt;/strong&gt; analyzes a page, displays its stability map, and helps create selectors. It doesn't block anything itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BlockIt&lt;/strong&gt; uses rules to locate and block unwanted elements on web pages.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder can also suggest special rules designed for use with BlockIt.&lt;/p&gt;

&lt;p&gt;In other words, one extension helps you investigate the DOM and prepare a rule, while the other applies that rule in practice.&lt;/p&gt;

&lt;p&gt;I'll cover BlockIt and its capabilities in a separate article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can StableHTMLFinder Be Useful Beyond Ad Blocking?
&lt;/h2&gt;

&lt;p&gt;Although the project grew out of the need to block unwanted elements, unstable DOM locators are a problem in other areas as well.&lt;/p&gt;

&lt;p&gt;One example is browser automation and UI testing.&lt;/p&gt;

&lt;p&gt;If a test relies on a dynamically generated class, it may fail after a page update even though the button or form it's supposed to interact with still exists.&lt;/p&gt;

&lt;p&gt;A stability map could potentially help developers identify more reliable reference points.&lt;/p&gt;

&lt;p&gt;Similar challenges sometimes arise when writing browser scripts or extracting data from websites.&lt;/p&gt;

&lt;p&gt;However, StableHTMLFinder isn't a universal solution for these scenarios. Its usefulness depends on the structure of the particular website and whether sufficiently stable characteristics can be identified.&lt;/p&gt;

&lt;p&gt;There's also an important distinction between observed stability and guaranteed future stability.&lt;/p&gt;

&lt;p&gt;Just because an element remained unchanged across several page loads doesn't mean it will never change.&lt;/p&gt;

&lt;p&gt;The stability map reflects the results of the analysis, not an absolute guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Think This Approach Matters
&lt;/h2&gt;

&lt;p&gt;When developing StableHTMLFinder, I wanted to change the way I worked with the DOM.&lt;/p&gt;

&lt;p&gt;Normally, a developer opens DevTools, sees an enormous tree of elements, and tries to figure out which characteristics are suitable for a selector.&lt;/p&gt;

&lt;p&gt;But much of that information may be useless: random classes, temporary IDs, dynamic attributes, and constantly changing containers.&lt;/p&gt;

&lt;p&gt;StableHTMLFinder takes a different approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, identify what remains stable across page loads. Then decide how to use those characteristics.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This doesn't eliminate every challenge associated with dynamic DOM structures, and it doesn't make selectors immune to future website changes.&lt;/p&gt;

&lt;p&gt;But it allows you to focus on more reliable information and reduce the amount of manual investigation required.&lt;/p&gt;

&lt;p&gt;And perhaps that best captures the original idea behind the project: not necessarily to write a selector for you, but to let you see a web page without the noise of constantly changing DOM characteristics.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try StableHTMLFinder
&lt;/h2&gt;

&lt;p&gt;StableHTMLFinder is a free Chrome extension.&lt;/p&gt;

&lt;p&gt;If you'd like to try it yourself, visit the &lt;strong&gt;&lt;a href="https://formacode.dev/en/stablehtmlfinder" rel="noopener noreferrer"&gt;official StableHTMLFinder project page&lt;/a&gt;&lt;/strong&gt;, where you'll find a link to install the extension from the Chrome Web Store.&lt;/p&gt;

&lt;p&gt;If you work with dynamic web pages, I'd be interested to hear what challenges you've encountered with selector stability and how you've approached solving them.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>dom</category>
    </item>
  </channel>
</rss>
