I need a method that, taken as parameters two nodes (node1 and node2), returns the minimum path that leads to node2 from node1.
Ideally, it returns an array of nodes, but for the moment it's OK to a string. So for example:
P
/ \
#text U
/ \
B I
| |
#text #text
function foo(node1, node2) {
...
}
when I run it in this way, for example on the nodes P (root) and B:
var res = foo(P, B);
console.log(res);
I obtain:
res = Array[3] {
0: P (class=..., id=...)
1: U (class=..., id=...)
2: B (class=..., id=...)
}
or, in the form of string:
res = "P(class=..., id=...) > U(class=..., id=...) > B(class=..., id=...)";
If the nodes have attributes (such as id or class), then returns even those (as in the example).
I searched the internet methods that did similar things but I found only methods that return the full path of the entire document and not between two nodes.
For example, I tried this method doesn't work for me because it returns the full path of a single node.
function getDomPath(el) {
var stack = [];
while ( el.parentNode != null ) {
console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
var sib = el.parentNode.childNodes[i];
if ( sib.nodeName == el.nodeName ) {
if ( sib === el ) {
sibIndex = sibCount;
}
sibCount++;
}
}
if ( el.hasAttribute('id') && el.id != '' ) {
stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
} else if ( sibCount > 1 ) {
stack.unshift(el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')');
} else {
stack.unshift(el.nodeName.toLowerCase());
}
el = el.parentNode;
}
return stack.slice(1); // removes the html element
}
Another thing, I would use pure JavaScript, no jQuery.
I have no idea how to do what I need, a your help would be greatly appreciated.
Thank you
<!DOCTYPE html>
<html>
<script>
window.onload = function() {
console.log(min_path(
document.getElementById("4"),
document.getElementById("9")
));
};
function min_path(node1, node2) {
if(node1 === node2) {
return node1;
}
var node_1_ancestors = get_ancestors(node1);
var node_2_ancestors = get_ancestors(node2);
var divergent_index = 0;
while(node_1_ancestors[divergent_index] === node_2_ancestors[divergent_index]) {
divergent_index++;
}
var path = [];
for(var i = node_1_ancestors.length - 1; i >= divergent_index - 1; i--) {
path.push(node_1_ancestors[i]);
}
for(var i = divergent_index; i < node_2_ancestors.length; i++) {
path.push(node_2_ancestors[i]);
}
return path;
}
function get_ancestors(node) {
var ancestors = [node];
while(ancestors[0] !== null) {
ancestors.unshift(ancestors[0].parentElement);
}
return ancestors;
}
</script>
</head>
<body>
<div id="0">
<div id="1">
<div id="2">
<span id="3"></span>
<span id="4">node1</span>
</div>
<div id="5">
<p id="6"></p>
<span id="7">
<div id="8">
<div id="9">node2</div>
<div id="10"></div>
</div>
</span>
</div>
</div>
<div id="11"></div>
</div>
</body>
</html>
Edit: It was going in to an infinite loop when the nodes were equal, so I added a check for that.
Related
I have a <div> element that contains both html elements and text. I want to find/remove the last or the last nth or the nth text only portion of it.
So for example
<div id="foo">
<span id="bar">abcdefg</span>
<span id="baz">z</span>
</div>
If I had a method to delete the last text character, the first call would delete z and the second call would delete g. Or if I had a method to find the 4th character, it would return d.
It sounds like you only care about the text nodes, so probably something like this so you can just delete the nth character:
var div = document.getElementById("foo");
const getTextNodes = (el, nodes) => {
nodes = nodes || [];
for (var i = 0; i < el.childNodes.length; i++) {
var curNode = el.childNodes[i];
if (curNode.nodeName === "#text") {
if (curNode.textContent.trim().length) {
nodes.push(curNode);
}
} else {
getTextNodes(curNode, nodes);
}
}
return nodes;
}
console.log(getTextNodes(div).map((el) => el.textContent));
const deleteNthCharacter = (el, n) => {
n--; // since we want to be "1 indexed"
const nodes = getTextNodes(el);
let len = 0;
for (let i = 0; i < nodes.length; i++) {
const curNode = nodes[i];
if (len + curNode.textContent.length > n) {
curNode.textContent = curNode.textContent.substring(0, n - len) + curNode.textContent.substring(n + 1 - len);
break;
} else {
len += curNode.textContent.length;
}
}
}
deleteNthCharacter(div, 2);
deleteNthCharacter(div, 7);
<div id="foo">
<span id="bar">abcdefg</span>
<span id="baz">z</span>
</div>
If I understood your question correctly this should do the trick:
function deleteLastChar(targetId){
const target = document.getElementById(targetId);
let lastWithText = -1;
//find last child that has text set
target.childNodes.forEach((child, iter) => {
if(child.innerText != undefined && child.innerText.length > 0){
lastWithText = iter;
}
});
// exit if no valid text node was found
if(lastWithText === -1)
return;
const lastNode = target.childNodes[lastWithText];
lastNode.innerText = lastNode.innerText.slice(0, -1);
}
deleteLastChar("foo")
deleteLastChar("foo")
deleteLastChar("foo")
deleteLastChar("foo")
<div id="foo">
<span id="bar">abcdefg</span>
<span id="baz">z</span>
</div>
If I understand the question this is probably what you're looking for
let x = document.getElementById('foo').children;
function erase() {
for (let i = x.length - 1; i >=0; i--) {
if(x[i].textContent.length > 0) {
const textC = x[i].textContent;
x[i].textContent = textC.substring(0, textC.length - 1);
return;
}
}
}
<div id="foo">
<span id="bar">abcdefg</span>
<span id="baz">z</span>
</div>
<button onclick="erase()">Erase</button>
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="foo">
<span id="bar">abcdefg</span><br>
<span id="baz">z</span><br><br>
<div id="result"></div>
<div id="result2"></div>
</div>
<script type="text/javascript">
var s = function(x){
return document.querySelector(x)
}
log = console.log;
var span1 = s("#bar")
var span2 = s("#baz")
var result = s("#result")
var result2 = s("#result2")
var res = span1.innerText.charAt(4)
// with the charAt method
result.innerText = " Result is : " +res+"\n\n"
// with regular Expression
var reg = /e/
result2.innerText = " Result2 is : " +span1.innerText.match(reg)
</script>
</body>
</html>
I put this example in the Quill Playground but am duplicating it here.
Input HTML
<div id="editor">
<ol>
<li>
<p>Outer Numbered para1</p>
<p>Outer numbered para2</p>
<ol>
<li>Inner Numbered List</li>
</ol>
</li>
</ol>
</div>
JavaScript
var quill = new Quill('#editor', {
theme: 'snow'
});
Output HTML
<div class="ql-editor" contenteditable="true">
<ol>
<li>Outer Numbered para1</li>
<li>Outer numbered para2</li>
<li class="ql-indent-1">Inner Numbered List</li>
</ol>
</div>
Question
Where did the nesting go? What am I doing wrong? How can I prevent the nesting from going away, or get it back?
Versions
Quill version 1.2.4
Chrome Version 58.0.3029.81 (64-bit)
Ubuntu 16.04 (64-bit)
you should normalize your nested list value when :
you want to insert nested value in your quilljs editor, here is some code maybe could help :
function normalizeNestedList(editorValue){
if(document){
let tempEditorNode = document.createElement('div');
tempEditorNode.innerHTML = editorValue;
const olElems = tempEditorNode.querySelectorAll('ol');
const ulElems = tempEditorNode.querySelectorAll('ul');
moveNestedList(olElems);
moveNestedList(ulElems);
return tempEditorNode.innerHTML;
}
}
function moveNestedList(ContainerListElems){
const quillIntentConst = `ql-indent-`;
let currentParentToInserted = null;
for(let i=0; i< ContainerListElems.length; i++){
const containerListElem = ContainerListElems[i];
const listDepth = getMaxParents(containerListElem);
if(listDepth > 0) {
const containerListElemChilds = containerListElem.childNodes;
for (let j = 0; j < containerListElemChilds.length; j++) {
if (containerListElemChilds[j].nodeName === 'LI') {
containerListElemChilds[j].setAttribute('class',`${quillIntentConst}${listDepth}`);
}
}
insertAfter(currentParentToInserted,containerListElem.outerHTML);
containerListElem.remove();
}else {
currentParentToInserted = containerListElem;
}
}
}
function getMaxParents(elem){
let parentNode = elem.parentNode;
let count = 0;
if(parentNode !== null && typeof parentNode !== 'undefined') {
let nodeName = parentNode.nodeName;
while (parentNode !== null && typeof parentNode !== 'undefined' && (nodeName === 'UL' || nodeName === 'OL' || nodeName === 'LI')) {
parentNode = parentNode.parentNode;
nodeName = parentNode.nodeName;
if(nodeName === 'UL' || nodeName === 'OL' ) {
++count;
}
}
}
return count;
}
function insertAfter(newNode, referenceNode) {
newNode.insertAdjacentHTML('afterend', referenceNode);
}
you can call normalizeNestedList function
Suppose you have an HTML page that contains sections in different languages, like this:
<html lang=en>
<div lang="th">
<p id="test1">ไทย</p>
</div>
<p id="test2">Implicitly English</p>
<div lang="en-CA">
<p id="test3">As Canadian as possible under the circumstances</p>
</div>
<p lang="en-AU"id="test4">Explictly Aussie</p>
</html>
Is there a direct way to discover which particular language code applies to a given HTML element? Something like:
// pseudo-code
var lang = myElement.getLang()
Here's what appears to be a very roundabout solution:
function getLang(element) {
var lang = element.getAttribute("lang")
if (!lang) {
var elements
, languages
, language
, ii
, selector
// Find all elements with an explicit lang attribute
elements = [].slice.call(document.querySelectorAll("*[lang]"))
// Determine which languages are present
languages = []
for (ii in elements) {
lang = elements[ii].getAttribute("lang")
if (languages.indexOf(lang) < 0) {
languages.push(lang)
}
}
lang = "" // reset
for (ii in languages) {
language = languages[ii]
selector = ":lang(" + language + ")"
elements = [].slice.call(document.querySelectorAll(selector))
if (elements.indexOf(element) > -1) {
if (lang.length < language.length) {
lang = language
}
}
}
}
return lang
}
Is there a more obvious way?
jsFiddle
I updated your fiddle with the following code, which you can run in this snippet. This simplifies it greatly.
function getLang(elem) {
var lang = "";
if (elem) {
var elements = [];
var queryResult = document.querySelectorAll("[lang]");
try {
//Wrapping in a try catch block to handle unsupported browsers.
elements = [].slice.call(queryResult);
} catch (error) {
for (var i = 0, len = queryResult.length; i < len; i++) {
elements.push(queryResult[i]);
}
}
if (elements.length > 0) {
//Find in the NodeList where the element is either itself or the first parent with lang attribute of the given element.
var matches = elements.filter(function(e) {
return e === elem || e.contains(elem);
}); //ES2015 -> elements.filter(e => e === elem || e.contains(elem));
var match = matches.length > 0 ? matches[matches.length - 1] : matches[0];
lang = match.lang ? match.lang : lang;
}
}
return lang;
}
var result = getLang(document.querySelector("#test1")) + " ";
result += getLang(document.querySelector("#test2")) + " ";
result += getLang(document.querySelector("#test3")) + " ";
result += getLang(document.querySelector("#test4"));
alert(result);
<body lang=en>
<div lang="th">
<p id="test1">ไทย</p>
</div>
<p id="test2">Implicitly English</p>
<div lang="en-CA">
<p id="test3">As Canadian as possible under the circumstances</p>
</div>
<p lang="en-AU" id="test4">Explictly Aussie</p>
</body>
My ultimate goal is to get out the information my website. I am trying to get something like this returned:
{
Goals: {
1: 'ET6',
2: 'ET10'
},
Sub-Off: 80,
Sub-On: 'ET1'
}
so I have the following markup (the huge line breaks are necessary):
<span class="stats jamie">
<img src="/client/images/icon-ball.gif" alt="Goals" width="13" height="13">
ET:6,ET:10
<img src="/client/images/suboff.gif" alt="Sub-Off" width="13" height="13">
80
<img src="/client/images/subon.gif" alt="Sub-On" width="13" height="13">
ET:1
</span>
What I have so far
$('.jamie').find('img').each(function(index){
console.info($(this).attr('alt'));
});
var stats = {};
$('.jamie img').each(function(){
var name = $(this).attr('alt');
var data = $(this)[0].nextSibling // Get the next node
.nodeValue // Get its text value
.trim() // Remove the extra spaces
.toLowerCase() // to lower case
.replace(/:/g,'') // remove colons
.split(','); // split on commas
stats[name] = data; // add to object
});
console.log(stats);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<span class="stats jamie">
<img src="/client/images/icon-ball.gif" alt="Goals" width="13" height="13">
ET:6,ET:10
<img src="/client/images/suboff.gif" alt="Sub-Off" width="13" height="13">
80
<img src="/client/images/subon.gif" alt="Sub-On" width="13" height="13">
ET:1
</span>
The accepted answer actually doesn't give you what you want. Here's an example using vanilla JS, jquery isn't needed to do a simple loop. This could be used if additional data is added you would just need to change the parent element and split the data with commas.
var parent = document.querySelectorAll('.stats img');
var obj = {};
for(var i in parent){
var img = parent[i];
var key = img.alt;
if(!img.nextSibling) break;
var values = img.nextSibling.data.trim().split(',');
if(values.length > 1){
obj[key] = {};
for(var c in values){
obj[key][c] = values[c];
}
} else{
obj[key] = values[0].toString();
}
}
console.log(obj)
a = {};
$('.jamie').find('img').each(function(){
var textVal = $.trim(this.nextSibling.nodeValue).replace(/:/,'').toLowerCase();
var textValArray = textVal.split(',');
var textValObj = {};
if( textValArray.length > 1 ){
$.map(textValArray , function( val, i ) {
textValObj[ i + 1 ] = val;
});
a[$(this).attr('alt')] = textValObj;
} else {
a[$(this).attr('alt')] = textVal;
}
});
console.log(a)
.spacer{height: 3000px; display: block; width: 100%;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<span class="stats jamie">
<img src="/client/images/icon-ball.gif" alt="Goals" width="13" height="13">
ET:6,ET:10
<img src="/client/images/suboff.gif" alt="Sub-Off" width="13" height="13">
80
<img src="/client/images/subon.gif" alt="Sub-On" width="13" height="13">
ET:1
</span>
We can only guess at the full parsing rules from the short example provided.
I expect you want something like this :
var obj = {};
$("span.stats img").each(function() {
var nextNode, key, val, text, arr;
nextNode = this.nextSibling;
if(nextNode.nodeType !== 3) { // test for text node
return true; // continue
}
key = $(this).attr('alt');
text = $.trim(nextNode);
arr = text.split(',');
if (arr.length < 2) {
val = text;
} else {
val = {};
for(var i=0; i<arr.length; i++ ) {
val[i] = arr[i].replace(':', '');
}
}
obj[key] = val;
});
I am trying to sort a table. I've seen several jQuery and JavaScript solutions which do this through various means, however, haven't seen any that use JavaScript's native sort() method. Maybe I am wrong, but it seems to me that using sort() would be faster.
Below is my attempt, however, I am definitely missing something. Is what I am trying to do feasible, or should I abandon it? Ideally, I would like to stay away from innerHTML and jQuery. Thanks
var index = 0; //Index to sort on.
var a = document.getElementById('myTable').rows;
//sort() doesn't work on collection
var b = [];
for (var i = a.length >>> 0; i--;) {
b[i] = a[i];
}
var x_td, y_td;
b.sort(function(x, y) {
//Having to use getElementsByTagName is probably wrong
x_td = x.getElementsByTagName('td')[index].data;
y_td = y.getElementsByTagName('td')[index].data;
return x_td == y_td ? 0 : (x_td < y_td ? -1 : 1);
});
A td element doesn't have a .data property.
If you wanted the text content of the element, and if there's only a single text node, then use .firstChild before .data.
Then when that is done, you need to append the elements to the DOM. Sorting a JavaScript Array of elements doesn't have any impact on the DOM.
Also, instead of getElementsByTagName("td"), you can just use .cells.
b.sort(function(rowx, rowy) {
x_td = rowx.cells[index].firstChild.data;
y_td = rowy.cells[index].firstChild.data;
return x_td == y_td ? 0 : (x_td < y_td ? -1 : 1);
});
var parent = b[0].parentNode;
b.forEach(function(row) {
parent.appendChild(row);
});
If the content that you're comparing is numeric, you should convert the strings to numbers.
If they are text strings, then you should use .localeCompare().
return x_td.localeCompare(y_td);
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>All Sorting Techniques</title>
<script type="text/javascript">
var a = [21,5,7,318,3,4,9,1,34,67,33,109,23,156,283];
function bubbleSort(a)
{
var change;
do {
change = false;
for (var i=0; i < a.length-1; i++) {
if (a[i] > a[i+1]) {
var temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
change = true;
}
}
} while (change);
document.getElementById("bublsrt").innerHTML = "Bubble Sort Result is: "+a;
}
var b = [1,3,4,5,7,9,21,23,33,34,67,109,156,283,318];
function binarySearch(b, elem){
var left = 0;
var right = b.length - 1;
while (left <= right){
var mid = parseInt((left + right)/2);
if (b[mid] == elem)
return mid;
else if (b[mid] < elem)
left = mid + 1;
else
right = mid - 1;
}
return b.length;
}
function searchbinary(){
var x = document.getElementById("binarysearchtb").value;
var element= binarySearch(b,x);
if(element==b.length)
{
alert("no. not found");
}
else
{
alert("Element is at the index number: "+ element);
}
}
function quicksort(a)
{
if (a.length == 0)
return [];
var left = new Array();
var right = new Array();
var pivot = a[0];
for (var i = 1; i < a.length; i++) {
if (a[i] < pivot) {
left.push(a[i]);
} else {
right.push(a[i]);
}
}
return quicksort(left).concat(pivot, quicksort(right));
}
function quicksortresult()
{
quicksort(a);
document.getElementById("qcksrt").innerHTML = "Quick Sort Result is: "+quicksort(a);
}
function numeric(evt){
var theEvent = evt || window.event;
var key = theEvent.keyCode || theEvent.which;
key = String.fromCharCode(key);
var regex = /[0-9]|\./;
if (!regex.test(key)) {
theEvent.returnValue = false;
if (theEvent.preventDefault)
theEvent.preventDefault();
}
}
function insertionsorting(a)
{
var len = a.length;
var temp;
var i;
var j;
for (i=0; i < len; i++) {
temp = a[i];
for (j=i-1; j > -1 && a[j] > temp; j--) {
a[j+1] = a[j];
}
a[j+1] = temp;
}
document.getElementById("insrtsrt").innerHTML = "Insertion Sort Result is: "+a;
}
function hiddendiv()
{
document.getElementById("binarytbdiv").style.display = "none";
document.getElementById("Insertnotbdiv").style.display = "none";
}
function binarydivshow()
{
document.getElementById("binarytbdiv").style.display = "block";
}
function insertnodivshow()
{
document.getElementById("Insertnotbdiv").style.display = "block";
}
function insertno(a)
{
var extrano = document.getElementById("Insertnotb").value;
var b= a.push(extrano);
var change;
do {
change = false;
for (var i=0; i < a.length-1; i++) {
if (a[i] > a[i+1]) {
var temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
change = true;
}
}
} while (change);
document.getElementById("insrtnosearch").innerHTML = "Sorted List is: "+a;
alert("Index of "+extrano +" is " +a.indexOf(extrano));
}
</script>
</head>
<body onload="hiddendiv()">
<h1 align="center">All Type Of Sorting</h1>
<p align="center">Your Array is : 21,5,7,318,3,4,9,1,34,67,33,109,23,156,283</p>
<div id="main_div" align="center">
<div id="bubblesort">
<input type="button" id="bubblesortbutton" onclick="bubbleSort(a)" value="Bubble Sort">
<p id="bublsrt"></p>
</div><br>
<div id="quicksort">
<input type="button" id="quicksortbutton" onclick="quicksortresult()" value="Quick Sort">
<p id="qcksrt"></p>
</div><br>
<div id="insertionsort">
<input type="button" id="insertionsortbutton" onclick="insertionsorting(a)" value="Insertion Sort">
<p id="insrtsrt"></p>
</div><br>
<div id="binarysearch">
<input type="button" id="binarysearchbutton" onclick="binarydivshow();" value="Binary Search">
<div id="binarytbdiv">
<input type="text" id="binarysearchtb" placeholder="Enter a Number" onkeypress="numeric(event)"><br>
<input type="button" id="binarysearchtbbutton" value="Submit" onclick="searchbinary()">
<p id="binarysrch">Sorted List is : 1,3,4,5,7,9,21,23,33,34,67,109,156,283,318</p>
</div>
</div><br>
<div id="Insertno">
<input type="button" id="insertno" onclick="insertnodivshow()" value="Insert A Number">
<div id="Insertnotbdiv">
<input type="text" id="Insertnotb" placeholder="Enter a Number" onkeypress="numeric(event);"><br>
<input type="button" id="Insertnotbbutton" value="Submit" onclick="insertno(a)">
<p id="insrtnosearch"></p>
</div>
</div>
</div>
</body>
</html>