I'm trying to write a Firefox extension that adds elements to the loaded page. So far, I get the root element of the document via
var domBody = content.document.getElementsByTagName("BODY").item(0);
and create the new elements via
var newDiv = content.document.createElement("div");
and everything worked quite well, actually. But the problems came when I added a button with on onclick attribute. While the button is correctly displayed, I get an error. I already asked asked here, and the answer with document.createElement() (without content) works.
But if I remove the 'content.' everywhere, the real trouble starts. Firstly, domBody is null/undefined, no matter how I try to access it, e.g. document.body (And actually I add all elements _after_the document is fully loaded. At least I think so). And secondly, all other elements look differently. It's seem the style information, e.g., element.style.width="300px" are no longer considered.
In short, with 'content.document' everything looks good, but the button.onclick throws an error. with only 'document' the button works, but the elements are no longer correctly displayed. Does anybody see a solution for that.
It should work fine if you use addEventListener [MDN] (at least this is what I used). I read somewhere (I will search for it) that you cannot attach event listener via properties when creating elements in chrome code.
You still should use content.document.createElement though:
Page = function(...) {
...
};
Page.prototype = {
...
addButton : function() {
var b = content.document.createElement('button');
b.addEventListener('click', function() {
alert('OnClick');
}, false);
},
...
};
I would store a reference to content.document somewhere btw.
The existing answer doesn't have a real explanation and there are too many comments already, so I'll add another answer. When you access the content document then you are not accessing it directly - for security reasons you access it through a wrapper that exposes only actual DOM methods/properties and hides anything that the page's JavaScript might have added. This has the side-effect that properties like onclick won't work (this is actually the first point in the list of limitations of XPCNativeWrapper). You should use addEventListener instead. This has the additional advantage that more than one event listener can coexist, e.g. the web page won't remove your event listener by setting onclick itself.
Side-note: your script executes in the browser window, so document is the XUL document containing the browser's user interface. There is no <body> element because XUL documents don't have one. And adding a button won't affect the page in the selected tab, only mess up the browser's user interface. The global variable content refers to the window object of the currently selected tab so that's your entry point if you want to work with it.
Related
I am trying to fill a textarea in a window opened with open(link_here) (on the same domain). It works fine in all browsers except IE/Edge.
Steps to reproduce here on stackoverflow:
var w = open('https://stackoverflow.com'); // example
// (allow popups and) wait for window to be opened, then:
let input = w.document.getElementsByClassName('f-input js-search-field')[0]
const prototype = Object.getPrototypeOf(input); // returns null on IE/Edge
Any workaround?
Thanks
I believe it is a Hierarchy access restriction. Since the element is not in the same window as the Object you are using, it doesn't have access to the object's information. You will have a somewhat similar problem if you try to append an element created by the main document and try to append it to the iframe document. When attempting this you will get a HierarchyRequestError.
Instead of using the main window's Object use the iframe window's Object:
var prototype = w.window.Object.getPrototypeOf(input);
console.log(prototype);
instead of vanilla javascript try JQUERY which works on every browser correct so change your selector to jquery one you need to include first JQUERY at your file
I am loading an ajax form with inputs that I apply .selectize() to. I run into issues when I close and reopen the form because there are instances of the Selectize constructor function that are still around.
Is there a way to remove these instances when I close the form? I can see these objects build up as I open and close the form by looking through firebug in the DOM under Selectize.count. How do I access these instances and destroy them?
I have tried this:
instance1[0].selectize.destroy();
instance2[0].selectize.destroy();
Assigned the variables like this:
instance1 = $('#preferences_sport').selectize({
//custom code
});
instance2 = $('#preferences_sport').selectize({
//custom code
});
The Selectize.count continues to build up and I am not sure where to go from here.
Here is a JSFiddle where I show the objects building up
So I see what you are saying now that the fiddle was added. I began by searching the documentation for that count property. I couldn't find it. So next I searched the source code since it seems this is some undocumented thing. The only count I could find in source code was this line:
eventNS : '.selectize' + (++Selectize.count),
So basically this explains it. That count while it does increase for every element this is called on is not a current count of running widgets. Its an internal property the guy who wrote this uses as a GUID for event namespaces. So for instance when you call destroy he can only remove the events specific to that instance of the widget.
I would not use this property to tell you anything. I think its safe to assume that your destroy is working fine. If you are unfamiliar with event namespacing you can read more about it here:
https://api.jquery.com/event.namespace/
You can see he uses that eventNS throughout the code to attach events if you search for it. jQuery does this in their code too for lots of stuff like in their event and data code. They have a GUID variable they use so anyone who loads more than one instance of jQuery on a page, the instances won't step on each others toes.
So I guess the only thing I would now ask you is where did you learn about this count property? If you just found it and assumed that it meant this was a count of running instances try to remember to always check with the documentation. If you found it in the docs then please point me towards now so I can take a look and see if it verifies what I found or requires more looking into.
Also as a bonus heads up, I saw this in your fiddle, input is a self closing tag or also known as void elements.
<input type="text" value="Calgary, Edmonton" class="selectize_this"></input>
Should just be:
<input type="text" value="Calgary, Edmonton" class="selectize_this" />
From the spec:
Void elements can't have any contents (since there's no end tag, no
content can be put between the start tag and the end tag).
Void elements: area, base, br, col, embed, hr, img, input, keygen,
link, meta, param, source, track, wbr
The Selectize API does expose the following method:
destroy()
Destroys the control and unbinds event listeners so that it can be garbage collected.
I am attempting to fire off an AJAX call based on the onclick event for a google map integration. The info_window_content function seen here: http://jsfiddle.net/6xw2y/ is the call to create the divs that reside within the map itself.
The "v" variable does in fact contain a store_id. So in the opening line of that function, it has the following:
var info_window_string = "<div class='maps_popup' id="+v.id+">";
Now I have an onclick event that I have duplicated and modified. The first onclick event works just fine and refreshes the panel as it should. The second onclick event doesn't work and the code for that is below:
$("#div").click(function(){
var store_id = $(this).find("div").attr("id");
var pathname = "ajax=1&store_id="+store_id+"&action=get_nearby_stores&distance="+distance+"&lat="+lat+"&lng="+lng+"&products="+$('#edit-products').val();
$("#pocp_content").load("file1.php?" + pathname);
});
That doesn't seem to work. I've also tried changing the div tag to be like this:
$("div").click(function(){
Which still doesn't work. As an added side hint. At one point I was able to get it to refresh but it was passing map-container as the store_id, instead of the id itself.
What am I missing here?
I agree with Joke_Sense10,
but I think you're probably not binding the event to the right DOM element.
Try to open up the developer console in your browser (while being on the side you develop this code for), and enter $("#div") to see if the element it returns is the one you expect. You can also use console.log($("#div")) in the code for that.
answer in comments
For a larger number of elements, always use .on() method as the latter will bind an single event listener on one of the topmost nodes in the DOM tree.
$(document).on("click","#"+v.id, function(){
I'm developing a website that allows users to open multiple pages of the same content in the same browser window via inline 'windows'.
As the content can be repeated multiple times the id's can in turn be the same and therefore I have to "handle" them each so that I can distinguish between these pages.
I currently do this by assigning on load a unique id to the script like so:
var id_key;
function load_page() {
id_key++;
load_script("test.js") //load javascript file
}
//test.js:
$(function () {
var unique_id = id_key;
//adds the `unique id ` to the end of all elements with an id attribute set. ie `mycontainer` becomes `mycontainer_1`
update_ids(unique_id);
$("#mybtn_ " + unique_id).click(function () {
//do stuff
});
}
This works fine most of the time however if multiple pages are loaded too fast the Id tends to get overwritten causing confusion and errors.
I am wondering if there is a better technique of this doing this. I have heard of backbone.js but I am not sure whether that would be helpful in this case.
There are several general approaches to solve this kind of problem:
Load the sub pages in iframes. Each iframe gets it's own ID space. Scripts in all frames can talk to each other via the parent variable as long as all documents were loaded from the same domain.
Don't use any ids. Instead, give each "window" an ID and then locate elements in the window via classes and parent-child relations. Note that an element can have more than one class.
You can then use $(selector, win) to look for elements on a certain window win. The window becomes the "Selector Context" which means jQuery will search only children of the window and nothing else.
At the start of your script, locate all important elements by ID and save them in a JavaScript object. That way, you can access them without using a jQuery selector.
For example, you could select anything with an ID and save it with .data() in the window element. After this setup, all elements would be accessible via $(win).data('id')
You can generate quite good unique ids by concatenating a date and a random number:
new Date().getTime() + Math.random()
While this is by no means perfect, I think in your use case it will suffice.
As Jack mentioned in his comment, you can pass this id to your new window as a get parameter. I once did a whole OS-like interface with this method, and it worked flawlessly.
Sorry for my english, I'm french :)
I created a Mootools class named "Slider".
This class has a "slider_element" attribute, which is a DIV element.
The class also has a "destroyer" method. This method destroys the DIV element.
The slider_element is supposed to be a div that contains another DIV with a "remove" CSS classname. When I click on the "remove DIV", I want the "destroyer" method to be called, so that the DIV disappears.
Here is my code below, and it works graphically like I want.
My question is : when I destroy the DIV element, I don't need no more my Slider instance (here "mySlider"). But my code destroys the DIV elements, not the slider instance. Do this instance still exist ? I suppose yes. So I searched how to destroy an instance of a class with Mootools, but didn't find ... so I supposed I'm doing something the wrong way, even if my code does what I want graphically. Please help :)
var Slider = new Class({
initialize: function(slider_element){
this.slider_element = slider_element;
this.slider_element.getElements('*[class="remove"]').addEvent('click', this.destroyer.bind(this));
},
destroyer: function(){
this.slider_element.destroy();
}
});
var myElement = $('my_slider');
var mySlider = new Slider(myElement);
(in reality, this is a simplified code, so that I don't disturb you with my whole code)
There is no way to explicitly destroy an object in JavaScript. The best you can do is to remove all references to it and hope that your JavaScript implementation reuses the memory.
So in principle you can just overwrite any references (like mySlider in your example) with null. But there can easily be 'implicit' references that you can't control, in closures for instance (as used for events) -- you might be able to help the garbage collector by 'cleaning out' any properties that reference other objects, before throwing it away, but then you have to make sure nothing bad happens if a reference survives somewhere and something tries to use those properties.
For elements, Mootools has the destroy method that goes through a whole DOM subtree and clears out all properties as well as the associated storage of the elements, such as event listeners, before removing it from the DOM.
In your case, as #Dimitar Christoff wrote, if you don't have any external code that calls methods on the Slider object, you don't need to save a reference to it in var mySlider.
And if you don't do that, the only thing that keeps the Slider object alive is the reference from the stack frame of the closure that is built by .bind(this) in the addEvent call. When the event fires and destroy is called, the event listener is removed, and the JavaScript engine is free to deallocate the Slider object as well.