views:

41

answers:

2

How can I match all elements that have a specific attribute that is added at run time?

For example, the code:

$('[onkeydown]')

will successfully match:

<div onkeydown="foo();">

but won't match the following div:

<div id="mydiv">
// ....
<script type="text/javascript">
var element = document.getElementById('mydiv');
element.onkeydown = foo;
</script>

Note: the ID attribute is used as an example. I do not know the ID's attribute values of all the elements I want to match, so please provide a solution which matches all elements that have an attribute specified.

To clarify, in the second example, the following code correctly returns the function that is used:

$('#mydiv').attr('onkeydown') // == foo

Note: if I do the same thing above, but instead of using an event (onkeydown), I use a different attribute which isn't an event (e.g. title), it will successfully match the dynamically added attributes. Thus, it seems like it is only a problem with events.

+2  A: 

[Working demo]

/**
* Returns all the elements with the specified attribute.
* @param base is optional (root element)
*/
function getElementsByAttr( attr, base ) {
  base = base || document;
  var all = base.getElementsByTagName('*'),
      len = all.length,
      res = [];
  for ( var i = 0; i < len; i++ )
    if ( all[i][attr] != null )
      res.push( all[i] )
  return res;
}

Please note that the function checks all elements in the DOM (if no root element is given) so it can become slow when used on heavy layouts.

galambalazs
This does indeed work. I was hoping there would be a jQuery solution.
Senseful
`$(':has([onkeydown])')` works, see my update.
galambalazs
@galambalazs: while that jQuery does match elements, it won't match the correct ones, since `:has` is looking for any descendants that have that attribute. So for example, it will always match my `<div>` tag's ancestors (e.g. `<body>`) but won't actually match the `<div>` tag itself. I'm just going to use your original code, which seems to work flawlessly. Thanks.
Senseful
well I'm not that into jQuery. Should've read more carefully the docs. The original one is still stable.
galambalazs
A: 

jQuery will not match attributes added after-the-fact in such a way. You need to use jQuery .is() or .has():

           <div id="mydiv">hi</div>
           <script type="text/javascript">
           var element = document.getElementById('mydiv');
           element.onkeydown = foo;
           alert($('[onkeydown]').length); // 0
           alert($("div").is("[onkeydown]")); // true
           </script>

However, I have to ask why you are trying to change the onkeydown or hard code it like that instead of using $("#mydiv").keydown(function () {});

Also, why not use jQuery to get the data in the first place? Why document.getElementById()?

element = $("#mydiv"); //This uses document.getElementById() inherently -- no speed loss
element.attr('onkeydown', foo); // kind of wasteful .. use .keydown()
tandu
According to @Senseful in the comment, the method *does* work when he uses `title` instead of `onkeydown`.
Pekka
Also, the OPs problem seems to be that a 3rd party component is changing the event handlers. That's the reason for the `getElementById()` example
Pekka
He doesn't have to use that; $("#") will do the same thing. What I am suggesting will work to remove the attribute too, e.g. $("#mydiv").function () { if ($(this).is("[onkeydown]")) { $(this).removeAttr('onkeydown');
tandu
Using `is` and `has` behave exactly the same. `is` will return `true` which shows that the attribute is set (similar to how `attr()` shows that the same event is set). `has` will not match the dynamically added attribute, similar to how my original matching query doesn't match it.
Senseful
@tandu: that code that sets the `keydown` dynamically is purely an example. The reason I can't match the div using its ID, is because as I specified in the question, I don't know the ID that will be used. Not only that, but I also don't know how many elements I need to modify.
Senseful
has will not match if the element itself has the attribute, but it will match children that have the attribute.
tandu
The ID is just an example too. You obviously know some level of element (selector) you need to use, so you will have to use that and go down. The other poster suggested * (everything).
tandu
Actually, I don't know the parent element, so I have to check for any of `<body>`'s children. I just tried `$('*').has('[onkeydown]').length` which returned `0`.
Senseful
You should use $("body") instead because $("*") will also search html/head elements. I'm not so sure about the has not working, so sorry about that, but that would not be very helpful anyway. You would have to iterate through each element and check whether it has onkeydown anyway.
tandu
@tandu: good point about using `body` instead of `*`, thanks.
Senseful