@@ -198,4 +198,108 @@ trustedSetAttr.details = {
198
198
world : 'ISOLATED' ,
199
199
} ;
200
200
201
+ /**
202
+ * @scriptlet remove-attr
203
+ *
204
+ * @description
205
+ * Remove one or more attributes from a set of elements.
206
+ *
207
+ * @param attribute
208
+ * The name of the attribute(s) to remove. This can be a list of space-
209
+ * separated attribute names.
210
+ *
211
+ * @param [selector]
212
+ * Optional. A CSS selector for the elements to target. Default to
213
+ * `[attribute]`, or `[attribute1],[attribute2],...` if more than one
214
+ * attribute name is specified.
215
+ *
216
+ * @param [behavior]
217
+ * Optional. Space-separated tokens which modify the default behavior.
218
+ * - `asap`: Try to remove the attribute as soon as possible. Default behavior
219
+ * is to remove the attribute(s) asynchronously.
220
+ * - `stay`: Keep trying to remove the specified attribute(s) on DOM mutations.
221
+ * */
222
+
223
+ export function removeAttr (
224
+ rawToken = '' ,
225
+ rawSelector = '' ,
226
+ behavior = ''
227
+ ) {
228
+ if ( typeof rawToken !== 'string' ) { return ; }
229
+ if ( rawToken === '' ) { return ; }
230
+ const safe = safeSelf ( ) ;
231
+ const logPrefix = safe . makeLogPrefix ( 'remove-attr' , rawToken , rawSelector , behavior ) ;
232
+ const tokens = rawToken . split ( / \s * \| \s * / ) ;
233
+ const selector = tokens
234
+ . map ( a => `${ rawSelector } [${ CSS . escape ( a ) } ]` )
235
+ . join ( ',' ) ;
236
+ if ( safe . logLevel > 1 ) {
237
+ safe . uboLog ( logPrefix , `Target selector:\n\t${ selector } ` ) ;
238
+ }
239
+ const asap = / \b a s a p \b / . test ( behavior ) ;
240
+ let timerId ;
241
+ const rmattrAsync = ( ) => {
242
+ if ( timerId !== undefined ) { return ; }
243
+ timerId = safe . onIdle ( ( ) => {
244
+ timerId = undefined ;
245
+ rmattr ( ) ;
246
+ } , { timeout : 17 } ) ;
247
+ } ;
248
+ const rmattr = ( ) => {
249
+ if ( timerId !== undefined ) {
250
+ safe . offIdle ( timerId ) ;
251
+ timerId = undefined ;
252
+ }
253
+ try {
254
+ const nodes = document . querySelectorAll ( selector ) ;
255
+ for ( const node of nodes ) {
256
+ for ( const attr of tokens ) {
257
+ if ( node . hasAttribute ( attr ) === false ) { continue ; }
258
+ node . removeAttribute ( attr ) ;
259
+ safe . uboLog ( logPrefix , `Removed attribute '${ attr } '` ) ;
260
+ }
261
+ }
262
+ } catch ( ex ) {
263
+ }
264
+ } ;
265
+ const mutationHandler = mutations => {
266
+ if ( timerId !== undefined ) { return ; }
267
+ let skip = true ;
268
+ for ( let i = 0 ; i < mutations . length && skip ; i ++ ) {
269
+ const { type, addedNodes, removedNodes } = mutations [ i ] ;
270
+ if ( type === 'attributes' ) { skip = false ; }
271
+ for ( let j = 0 ; j < addedNodes . length && skip ; j ++ ) {
272
+ if ( addedNodes [ j ] . nodeType === 1 ) { skip = false ; break ; }
273
+ }
274
+ for ( let j = 0 ; j < removedNodes . length && skip ; j ++ ) {
275
+ if ( removedNodes [ j ] . nodeType === 1 ) { skip = false ; break ; }
276
+ }
277
+ }
278
+ if ( skip ) { return ; }
279
+ asap ? rmattr ( ) : rmattrAsync ( ) ;
280
+ } ;
281
+ const start = ( ) => {
282
+ rmattr ( ) ;
283
+ if ( / \b s t a y \b / . test ( behavior ) === false ) { return ; }
284
+ const observer = new MutationObserver ( mutationHandler ) ;
285
+ observer . observe ( document , {
286
+ attributes : true ,
287
+ attributeFilter : tokens ,
288
+ childList : true ,
289
+ subtree : true ,
290
+ } ) ;
291
+ } ;
292
+ runAt ( ( ) => { start ( ) ; } , behavior . split ( / \s + / ) ) ;
293
+ }
294
+ removeAttr . details = {
295
+ name : 'remove-attr.js' ,
296
+ aliases : [
297
+ 'ra.js' ,
298
+ ] ,
299
+ dependencies : [
300
+ runAt ,
301
+ safeSelf ,
302
+ ] ,
303
+ } ;
304
+
201
305
/******************************************************************************/
0 commit comments