When we say, DOM is loaded , I mean the DOM but not the page.
What happens in the browser! When DOM is loaded? Please can you be more precise.
thank you
"When the DOM is loaded but not the page" doesn't really mean much. As the HTML is loaded, the browser renders it as the static page you see on your screen. The DOM is the representation that allows interaction with those elements.
For example, I can create objects in JavaScript and then manipulate them, but I've only manipulated a simple object. A DOM object, looks like the same object, but it's tied to a correlating [X|XH|H]TML object; so that when I call a method on this object, it didn't just interact with a container of information but its constituent node on the page. Consequently, you can't use a DOM method on an element that hasn't been rendered yet.
Tangible example:
/* Manipulating a standard JavaScript object */
var obj = { firstProp: "InitialValue",
secondProp: "somethingelse",
aMethod: function(){ this.firstProp = "Changed" }
}
console.log(obj.firstProp); // Ouput is "InitialValue"
obj.aMethod();
console.log(obj.firstProp); // Output is "Changed", but nothing is effected other than that value
/* Here is a method called on a DOM element; pretend it's an input/text */
var obj2 = document.getElementById("testId");
obj2.value = "This is your new text box value";
In the second example, you see that I didn't just change an arbitrary object's value. I've change the HTML's rendered representation of that object.
That's the best way I can think to explain it at the moment.
Related
I have a section of my view (html) that is generated programmatically by a viewmodel/class. This uses the Aurelia DOM (Aurelia Docs - pal :: Dom) functionality to generate and add the raw HTML elements to the view.
However, I am unable to get events within the generated html to call back to the viewmodel. An example:
let deleteButton = this.dom.createElement("button");
deleteButton.setAttribute("onclick", "cancelCreditNote(`${ row.creditNoteId }`)");
A click on the generated button won't call back to the viewmodel, which does have a cancelCreditNote function. Various other things like deleteButton.setAttribute("click.delegate", "cancelCreditNote('${ row.creditNoteId }')"); do not work either.
Does anyone know how to access a viewmodel class from essentiall 'raw' html in aurelia?
Unfortunately in this instance I cannot use the standard aurelia templating to generate the HTML.
The DOM property on PAL is just an abstraction for the browser's DOM object, create element is likely just calling document.createElement which doesn't afford any Aurelia binding to the created element.
You could try using aurelia.enhance(context, element) which takes an existing DOM element and runs it through the templating engine.
With this method you can also pass a binding context to apply to the element.
In my HTML I use this:
<div id="collapsesidebar" click.delegate="toggleSidebar()">
In my view-model I have this method:
toggleSidebar(){
alert('hi');
}
You could also do this from your view-model with JQuery like this:
attached() {
$('main').on('click', ()=> alert('hi'));
}
The last option is ONLY available áfter the attached() method is triggered: before that the binding needs to do its job and only after that the elements are located inside of the dom.
In other words: this will not work:
activate(){
$('main').on('click', ()=> alert('hi'));
}
because the constructor and the activate method both get fired before the attached method.
I have a simple XML view (fragment) like this:
<html:div id="holder"></html:div>
I want to add content programmatically like this:
var holder = this.byId("holder");
var label = new sap.m.Label({
text: "Label"
});
holder.addContent(label);
Effect is nothing, no error, no added content.
Why does it not work?
This is because content is not an aggregation (an easy mistake to make, since content usually is an aggregation).
sap.ui.core.HTML's content metadata object is a property of type string. From the jsdoc:
HTML content to be displayed, defined as a string.
You will need to use a different container for your label, such as sap.ui.layout.VerticalLayout, or you could just use raw HTML to stick in your holder object, rather than that sap.m.Label type.
Here is a jsbin that takes the XML view part of this question out of the equation.
Note: See #hirse's comment below for an important distinction when using html:div in XML views
The HTML element and the UI5 Controls are not directly compatible. UI5 Controls are JavaScript objects that have a render function. The render function creates a html fragment on demand. That html fragment ist then inserted into the page.
I have never tried it, but a solution could be to use the placeAt() method of your label:
label.placeAt("holder");
If you are using an XML View, the holder id will be prefixed. Then you should use something like this:
label.placeAt(this.getView().createId("holder"));
You can get DOM element of UI5 control by using getDomRef of sap.ui.core.Element class.
Then add your content to this DOM element by using placeAt()
Here is working example.
What is the difference between them? Devdocs says that mousedown is part of the dom level 3 events (not entirely sure what this means either) but windows.mousedown is a separate page on devdocs. Are the two different somehow?
window is an object. An object contains information (values). The values in an object are formatted into name:value pairs, or often referred to as key:value, or property:value. If you see curly braces in JavaScript code {}, that's an object. If you log something to the web browsers window, console.log("some text" + myVariable); and see [object Object], then myVariable is an object. You can add a property:value pair to an object with: objName.property = value. window is an object. It's an object with values in it from your browsers window. When a function is assigned to window.somename:
window.mousedown = function() {statements here;}
That is putting the function into the window object. Objects can contain all sorts of stuff: other objects, arrays, values, and even functions. I'm explaining some background info for the sake of a more complete understanding.
Here is some documentation from Mozilla on functions and function scope:
Functions and Scope Mozilla
There is also ON mousedown
window.onmousedown
Is an event handler for the onmousedown event.
Window.onmousedown event handler
A function can be assigned to an event.
window.onmousedown will detect a mouse down event anywhere in the document. If you want to detect a mouse down event specific to a certain element, you'd probably put it into a button, or a image, or a input tag.
<label onmousedown='fncSendMail()'>
Example:
<script>
window.onmousedown = mousedown;
function mousedown() {
alert("mousedown event detected!");
};
</script>
<p>click anywhere to fire the mousedown event</p>
In the above example, onmousedown and mousedown are two different things. onmousedown is an event. mousedown is the name of the function.
So, what's the difference between mousedown and window.mousedown? window.mousedown is being added as a property:value pair to your browsers window object, mousedown isn't.
The DOM is the Document Object Model. It allows manipulation of the Document (Your web page.) The DOM is an API. It's an interface, which means that it's in between your code and your HTML allowing a connection between the two. Document Object Model Level 3
DOM mouseup W3.org
DOM mouse events
I know there is .on and .live (deprecated) available from JQuery, but those assume you want to attach event handlers to one ore more events of the dynamically added element which I don't. I just need to reference it so I can access some of the attributes of it.
And to be more specific, there are multiple dynamic elements like this all with class="cluster" set and each with a different value for the: title attribute, top attribute, and left attribute.
None of these jquery options work:
var allClusters = $('.cluster');
var allClusters2 = $('#map').children('.cluster');
var allClusters3 = $('#map').find('.cluster');
Again, I don't want to attach any event handlers so .on doesn't seem like the right solution even if I were to hijack it, add a bogus event, a doNothing handler, and then just reference my attributes.
There's got to be a better solution. Any ideas?
UPDATE:
I mis-stated the title as I meant to say that the elements were dynamically added to the DOM, but not through JQuery. Title updated.
I figured it out. The elements weren't showing up because the DOM hadn't been updated yet.
I'm working with Google Maps and MarkerClustererPlus to give some more context, and when I add the map markers using markerclustererplus, they weren't available in the javascript code following the add.
Adding a google maps event listener to my google map fixed the problem:
google.maps.event.addListener(myMarkerClusterer, 'clusteringend', function () {
// access newly added DOM elements here
});
Once I add that listener, all the above JQuery selectors and/or methods work just fine:
var allClusters = $('.cluster');
var allClusters3 = $('#map').find('.cluster');
Although this one didn't, but that's because it only finds direct decendants of parent:
var allClusters2 = $('#map').children('.cluster');
Do what you need to do in the ajax callback:
$.ajax(...).done(function (html) {
//append here
allClusters = $('.cluster');
});
If you want them to be separate, you can always bind handlers after the fact, or use $.when:
jqxhr = $.ajax(...).done(function (html) { /* append html */ });
jqxhr.done(function () { allClusters = $('.cluster') });
$.when(jqxhr).done(function () { /* you get it */ });
If these are being appended without ajax changes, then just move the cluster-finding code to wherever the DOM changes take place.
If that's not an option, then I guess you would just have to check on an interval.
So I have a TinyMCE form on my page and it is pre-filled with "sections" (divs with specific class names).
I have a couple of plugins that will add to TinyMCE with more "sections".
I need it so when I push the plugin button it will test to make sure the cursor is not inside a "section" and paste a "section" inside another "section".
Not sure the direction I need to take to accomplish this. Any help would be great.
more info:
So below is an example of a plugin that adds a button that just inserts a simple dov into the editor at the selection/cursor.
ed.addButton('pluginbutton', {
title : 'MyPlugin',
image : 'img/test.png',
onclick : function() {
ed.selection.setContent('<div>test</div>');
}
});
I am currently thinking that onBeforeSetContent is the API event handler I need to set to process whether or not I am in another section and if so send a message to the screen. If not just do the setContent method. I am not sure exactly how to set that up though so I am still figuring that out. Any help here?
Since it seems like you have control over the plugin, here is how I would edit it to work.
Note: I am using the jQuery method closest. I figured since you are on the jQuery core team, you are probably using it for this project. If not, just refactor that line as needed. The important part is that selection.getNode() returns the DOM element that is the parent of both the start and end selection points.:
ed.addButton('pluginbutton', {
title : 'MyPlugin',
image : 'img/test.png',
onclick : function() {
if( !$(ed.selection.getNode()).closest('div.section').length ){
ed.selection.setContent('<div class="section">test</div>');
}
}
});
Additional thoughts
Also, to make your plugin aware enough so it won't put a div as the child of a p tag, you could do something like this:
Replace onclick above with this:
onclick: function(){
var $node = $(ed.selection.getNode());
if( !$node.closest('div.section').length ){
// Get highest element that is a direct child of the `body` tag:
var $parent = $node.closest('body > *');
// Wrap with our special section div
if($parent.length) $parent.wrap('<div class="section"></div>');
}
}
I don't know TinyMCE specifically, but it should be possible to extract the current DOM element from ed.selection. If that is possible (I'm sure it is using some sort of getter function or property), you should be able to do the following:
Mark a "forbidden" area using an id or class ("<div class='protected'> ")
traverse through the selection's ancestry (using the parentNode property of the element) and check whether one of the parent elements has the "protected" class or ID.
If the ID was found, do not execute setContent(); otherwise execute it.