5

It may not be common knowledge, but "Javascript on many (all?) modern browsers seems to create variables on the window object for DOM elements with IDs".

Knowing this I'd like to be able to delete these variables and below is some code I've tried without success. Also consider my screenshot of console.log statements, which first indicates why as not being a property of window (it should come in between "webkitUrl" and "window"), but nevertheless in the two console.log statements that immediately follow the first, window/why is shown as the div from the document?

Why can't these automatically generated variables be deleted from their parent object, just like any other?

<!DOCTYPE html>

<html>
<head>
    <script>
    setTimeout(function() { //poor man's document/ready
        var allElements = document.getElementsByTagName("*"), elementId;

        for (var i=allElements.length; i--; ) {
            elementId = allElements[i].id;

            if (elementId && window[elementId] instanceof HTMLElement) {
                delete window.why;
                console.log(window);
                console.log(window.why);
                console.log(why);
            }
        }
    });
    </script>
</head>

<body>
<div id="why"></div>
</body>

</html>

enter image description here

Community
  • 1
  • 1
Dexygen
  • 12,287
  • 13
  • 80
  • 147
  • 3
    1) Why would you want to do that? 2) `non-configurable` properties can't be deleted. – 4castle Jul 05 '16 at 17:58
  • `delete` only works on "own" properties, not inherited ones. The elements may be located further down the prototype chain. –  Jul 05 '16 at 17:58
  • Can't be done, and shouldn't be an issue. – adeneo Jul 05 '16 at 18:00
  • 1
    Interesting that in Firefox, `Object.getPrototypeOf(Object.getPrototypeOf(window)).hasOwnProperty("foobar")` is `true` and `.getOwnPropertyDescriptor()` lists it as `configurable` and `writable`, yet I can't delete it or write to it. –  Jul 05 '16 at 18:01
  • Why would you want them deleted? Are they breaking something? – IMTheNachoMan Jul 05 '16 at 18:04
  • Seems you can however shadow it, so assigning `undefined` may be enough for you. –  Jul 05 '16 at 18:07
  • 1
    @squint Interesting, in my test in Chrome, it *did* delete it (returned true), but is still there afterwards. [JSFiddle Demo](https://jsfiddle.net/0jhkL03o/) – 4castle Jul 05 '16 at 18:10
  • 1
    @squint exactly, it's still there afterwards. To everybody else, I thought this was going to cause a problem with strict mode not knowing whether `why` was assigned to or not, but it will indeed complain if you try to assign to why – Dexygen Jul 05 '16 at 18:14
  • Why would it complain, you can overwrite the properties of window that contains named nodes with anything, even in strict, but if you delete it, the DOM updates the properties, and they're back .. – adeneo Jul 05 '16 at 18:18
  • @GeorgeJempty: Firefox does not complain in strict mode unfortunately. However, you can work around that by assigning a read-only property. `Object.defineProperty(window, "foobar", {value: undefined})`. Different error, but better than nothing. –  Jul 05 '16 at 18:18
  • @squint It says it's configurable because if you remove the element with that ID, the property will no longer appear to be there. Saying non-configurable would violate an [invariant](http://www.ecma-international.org/ecma-262/6.0/#sec-invariants-of-the-essential-internal-methods). This doesn't mean you can remove it, because in fact there is no such property, it's just a proxy. – Oriol Jul 05 '16 at 20:08
  • @Oriol: Ah, got it. Thanks! –  Jul 05 '16 at 20:21

2 Answers2

5

That's because these properties are not directly stored in window. Instead, it behaves like a proxy.

For example, see what Firefox does when you use getOwnPropertyDescriptor on WindowProperties (from which window inherits):

bool WindowNamedPropertiesHandler::getOwnPropDescriptor(
  JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
  bool /* unused */, JS::MutableHandle<JS::PropertyDescriptor> aDesc
) const {
  // ...
  Element* element = document->GetElementById(str);
  if (element) {
    JS::Rooted<JS::Value> v(aCx);
    if (!WrapObject(aCx, element, &v)) {
      return false;
    }
    FillPropertyDescriptor(aDesc, aProxy, 0, v);
    return true;
  }
  // ...
}

You might think that when you set an ID to some element, Firefox stores it as a global property. But it doesn't work like this: it's when you attempt to access the property that Firefox will use GetElementById to know if there is some element with that ID or not, and answer accordingly.

Even more, deletions are explicitly forbidden:

bool WindowNamedPropertiesHandler::delete_(
  JSContext* aCx, JS::Handle<JSObject*> aProxy,
  JS::Handle<jsid> aId, JS::ObjectOpResult &aResult
) const {
  return aResult.failCantDeleteWindowNamedProperty();
}

This behavior is hard-coded and you can't prevent it. So if these properties annoy you, just override them by declaring your own variables.

Oriol
  • 274,082
  • 63
  • 437
  • 513
4

I'd like to be able to delete these variables

There's absolutely no reason to do that. These globals are there for backwards compatibility and browsers hardly will remove them. However, they were created in a way so that you can simply ignore them - they won't interfere with any of your variables.

Just declare var why; in your script and the reference to the element is gone.

Why can't these automatically generated variables be deleted from their parent object?

First of all, variables cannot be deleted in general. Only object properties can - it's just that some global variables are deleteable properties of the global object.

The element references probably can't be deleted because a) they're not real properties but rather proxied b) they're not properties of the global object itself, but of its prototype or c) they're properties of an extra variable record in the global scope. Just consider them to be magic and don't care any more.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375