First look at the concept of the DOM is a tree, its root node is document, can be roughly shown in the following figure:
The so-called "most recent common ancestor element" refers to a given set of elements that find the most depth in the tree, but also the elements of the ancestor elements of all of these elements.
For example, the results of C,g and H of the results of the I and G in the above figure are a,d and E, and the result of Html,c and B is HTML.
Test driver
For the problem of partial logic, and do not have full grasp of the function is correct, so still first constructs the test yongming, seeks to let the function pass the test.
This is the structure of the above diagram as a DOM structure, a said Body,b head, the other nodes are using the div element, and the above mentioned as the test input and output, first constructs the test:
Copy Code code as follows:
function Test () {
var result;
result = Find (' i ', ' G ');
Result.id!== ' C ' && alert (' Fail (i, g) ');
result = Find (' g ', ' h ');
Result.id!== ' A ' && alert (' Fail (g, h) ');
result = Find (' d ', ' e ');
Result.nodeName.toLowerCase ()!== ' HTML ' && alert (' Fail (d, e) ');
result = Find (' C ', ' B ');
Result.nodeName.toLowerCase ()!== ' HTML ' && alert (' Fail (c, b) ');
}
Basic logic
The logic of this time is roughly this:
1. For each given element, iterate up from the parent element to the document in turn.
2, to traverse the process of each element, save to an ordered map, with elements as the key to the number of times to traverse to the value.
2, finally traversing the map, find the same as the first value and the number of given elements of the same item, is the first by all the elements of the traversal through the element, also known as the most recent common ancestor elements.
Detail issues
In the actual process, the construction of the map is more important, there are 2 problems involved:
1. Map cannot directly use elements as keys and must be converted to appropriate primitive types (such as number, string, regex, etc.).
2. Chrome will automatically sort the keys in the object, so try to avoid using the number type as a key.
For the first question, the element must be bound to a suitable field, playing the role of a unique identifier. Fortunately, the HTML5 provides the data-* attribute, so that the DOM's metadata-carrying capacity has been greatly improved, you can boldly add the desired attributes.
For the second problem, itself is not difficult, the generated identifier to avoid number on the line, the convenient way is to add an underscore, or use string.fromcharcode into characters, no matter what.
Implementation code
Code is a bit long, mainly personal prefer to the style of the Java, each of the statements each branch is clear, do not like to use && or | | To deal with the conditional branch, so there are a lot of rows with only one curly brace and the like, but the really effective code is still streamlined. Lazy to install similar toggle such as Plug-ins, do not want to see the scroll bar, just throw it here.
Copy Code code as follows:
function Find () {
var length = Arguments.length,
i = 0,
node,//Current nodes
Parent,//Parental node
Counter = 0,
UUID,//Unique identifier to the DOM
hash = {}; The final result of the map
For each element, traverse up to document
This double loop is inevitable.
for (; i < length; i++) {
Get node
node = arguments[i];
if (typeof node = = ' String ') {
node = document.getElementById (node);
}
Traverse up
while (parent = Node.parentelement | | node.parentnode) {
Stop at document, or it's a dead loop.
if (Parent.nodetype = = 9) {
Break
}
Get or add an identifier
UUID = Parent.getattribute (' Data-find ');
if (!uuid) {
UUID = ' _ ' + (++counter); Avoid chrome hash reordering
Parent.setattribute (' Data-find ', uuid);
}
Increase Count
if (Hash[uuid]) {
hash[uuid].count++;
}
else {
Hash[uuid] = {node:parent, count:1};
}
node = parent;
}
}
The hash contains only the nodes up and down through the parent node, should not be very large
So this cycle is faster.
For (i in hash) {
if (Hash[i].count = length) {
return hash[i].node;
}
}
};
Comments
Test is no problem, but the test case is perfect is not good to say, expect netizens to help me find out the problem, for the logical type is really no confidence to say 100% no problem.
For the parent element, habitually compatible IE is written parentelement | | ParentNode, this is because of a bug in IE that parentnode does not exist when an element has just been created but has not entered the DOM. However, this function ensures that nodes are in the DOM tree, but parentelement is not necessary. So sometimes customary compatible code is not necessarily a good thing, the most suitable for the current environment code is good code.
Although the 2 cycle is unavoidable, but the total faint sense of the cycle there is a way to do less in a particular case, such as the upward traversal when found that an element is not a common ancestor of all elements, then do not increment the count value.
Last for.. In the loop is there any way to dispense with it? Is there a way to keep the most appropriate node in real time through a variable in the 2 cycle above?