Efficient way for counting html characters using JavaScript - javascript

I'm trying to find total character count (offset) from a starting to selected/clicked element/text. For example,
<div>
<span>text 1</span>
<span>text 2</span>
<span>text 3</span>
</div>
if someone clicks on text 2, I need to count total characters of all elements before text 2.
So here is my approach:
(The problem with my code is, it crashes the browser if there are
lots of nested elements, so what could be other efficient way around?)
const root = document.querySelector("#root")
root.addEventListener("click", function() {
const selection = window.getSelection()
const startOffset = selection.getRangeAt(0).startOffset;
const node = selection.getRangeAt(0).startContainer;
const totalOffset = countTotalOffset(node, startOffset);
alert('total offset: ' + totalOffset )
})
function countTotalOffset(node, offset) {
if (!node.previousSibling) {
if (!node.parentElement) return offset;
if (node.parentElement.id === "root") return offset
return countTotalOffset(node.parentElement, offset)
}
const _offset = !node.previousSibling.textContent ? 0 : node.previousSibling.textContent.length;
return countTotalOffset(node.previousSibling, offset + _offset);
}
<h2>Click on any list item to get offset count</h2>
<ul id="root">
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
<li>
list 4
<ul>
<li>list 5</li>
<li>list 6</li>
<li>list 7</li>
<li>list 8
<ul>
<li>list 9</li>
<li>list 10</li>
<li>list 11</li>
<li>list 12
<ul>
<li><span>list 13</span> <span>list 13</span></li>
<li>list 14</li>
<li>list 15
<ul>
<li>list 16</li>
<li><span>list 17</span> <span>list 17</span></li>
<li>list 18</li>
<li>list 19</li>
<li><span>list 20</span> <span>list 20</span></li>
<li>list 21</li>
</ul>
</li>
<li>list 22</li>
<li>list 23</li>
<li>list 24</li>
</ul>
</li>
<li>list 25</li>
<li>list 26</li>
</ul>
</li>
<li>list 27</li>
<li>list 28</li>
</ul>
</li>
<li>list 29</li>
<li>list 30</li>
</ul>

It's not beautiful, but you can try this.
It's about adding a unique placeholder text to your element, then use "innerText" to get all the document text and count everything until you meet the placeholder. The only problem is that it introduces a "magic word".
const root = document.querySelector("#root")
root.addEventListener("click", function() {
const selection = window.getSelection()
const originalText = selection.anchorNode.textContent
const placeholder = "$$$_placeholder_$$$"
selection.anchorNode.textContent = originalText + placeholder
const charsCount = root.innerText.split(placeholder)[0].length
selection.anchorNode.textContent = originalText
alert(charsCount)
})
<ul id="root">
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
<li>
list 4
<ul>
<li>list 5</li>
<li>list 6</li>
<li>list 7</li>
<li>list 8
<ul>
<li>list 9</li>
<li>list 10</li>
<li>list 11</li>
<li>list 12
<ul>
<li><span>list 13</span> <span>list 13</span></li>
<li>list 14</li>
<li>list 15
<ul>
<li>list 16</li>
<li><span>list 17</span> <span>list 17</span></li>
<li>list 18</li>
<li>list 19</li>
<li><span>list 20</span> <span>list 20</span></li>
<li>list 21</li>
</ul>
</li>
<li>list 22</li>
<li>list 23</li>
<li>list 24</li>
</ul>
</li>
<li>list 25</li>
<li>list 26</li>
</ul>
</li>
<li>list 27</li>
<li>list 28</li>
</ul>
</li>
<li>list 29</li>
<li>list 30</li>
</ul>

Related

Load content on anchor link click

I have content that is hidden by default. On button click, I want the selected one to show its content. I.e if find more is clicked on the second div, show the content for that div.
Approach so far:
function showClass(a) {
var e = [];
var e = document.getElementsByClassName(a);
for (elem of e) {
if (!elem) return true;
if (elem.style.display == "none") {
elem.style.display = "block"
} else {
elem.style.display = "none"
}
}
return true;
}
.list {
display: none;
}
a {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="list">
<ul>
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
</ul>
</div>
<div class="list">
<ul>
<li>list 4</li>
<li>list 5</li>
<li>list 6</li>
</ul>
</div>
<div class="list">
<ul>
<li>list 7</li>
<li>list 8</li>
<li>list 9</li>
</ul>
</div>
<a onclick="showClass('list');" class="loadMoreBtn">Find out more</a>
Where am I going wrong?
You have a few issues:
You should use document.getElementsByClassName(a); (note the document)
To iterate through a HTMLCollection (returned by getElementsByClassName) you should use .forEach, for...of or a regular for loop. for...in is not a recommended way to iterate a collection as it is made to iterate over properties in an object. Thus, the for..in loop can give unexpected properties of the HTMLCollection such as its length when using it to iterate (when instead all you're after is the node)
function showClass(a) {
// var e = []; <-- no need for this \/ redeclared below
var e = document.getElementsByClassName(a);
for (elem of e) {
if (!elem) return true;
if (elem.style.display == "none") {
elem.style.display = "block"
} else {
elem.style.display = "none"
}
}
return true;
}
.list {
display: none;
}
a {
cursor: pointer;
}
<div>
<ul class="list">
<p>sample text</p>
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
</ul>
</div>
<div>
<ul class="list">
<li>list 4</li>
<li>list 5</li>
<li>list 6</li>
</ul>
</div>
<div>
<ul class="list">
<p>sample text</p>
<li>list 7</li>
<li>list 8</li>
<li>list 9</li>
</ul>
</div>
<a onclick="showClass('list');" class="loadMoreBtn">Find out more</a>
To have separate buttons for each item/div you can give each item an id, and then toggle the id by passing it through as an argument:
function showClass(id) {
var elem = document.getElementById(id);
var visible = getComputedStyle(elem).display == "block";
if (!visible) {
elem.style.display = "block"
} else {
elem.style.display = "none"
}
}
.list {
display: none;
}
a {
cursor: pointer;
}
<div>
<ul class="list" id="list-one">
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
</ul>
</div>
<div>
<ul class="list" id="list-two">
<li>list 4</li>
<li>list 5</li>
<li>list 6</li>
</ul>
</div>
<div>
<ul class="list" id="list-three">
<li>list 7</li>
<li>list 8</li>
<li>list 9</li>
</ul>
</div>
<a onclick="showClass('list-one');" class="loadMoreBtn">Find out more - Item 1</a>
<br />
<a onclick="showClass('list-two');" class="loadMoreBtn">Find out more - Item 2</a>
<br />
<a onclick="showClass('list-three');" class="loadMoreBtn">Find out more - Item 3</a>
Since you've tagged the question with jQuery and you're including it, you should really use it for this since it's very simple, concise and readable code...
function toggleClass(className) {
$("." + className).toggle();
}
.list {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"> </script>
<div>
<ul class="list">
<p>sample text</p>
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
</ul>
</div>
<div>
<ul class="list">
<li>list 4</li>
<li>list 5</li>
<li>list 6</li>
</ul>
</div>
<div>
<ul class="list">
<p>sample text</p>
<li>list 7</li>
<li>list 8</li>
<li>list 9</li>
</ul>
</div>
<a onclick="toggleClass('list');" class="loadMoreBtn">Find out more</a>
As you can see, jQuery has a toggle() method that toggles visibility, and jQuery methods work on arrays of elements by default, so you don't need a loop and you don't need to check the current visible state of anything.
You could also go a bit further and assign the event handler in Javascript, rather than inline in the anchor element, like this...
$(".loadMoreBtn").on("click", function() {
$(".list").toggle();
});
Keeping your Javascript out of HTML is encouraged, as it means you don't have to hunt different files to find it, which is great for both you and anyone else that ever has to look at your code.
Please have look at below code i have madden few changes in your code
function showClass(a) {
var e = [];
var e = document.getElementsByClassName(a);
console.log(e);
for (i=0;i<=e.length;i++) {
if (!e[i]) return true;
e[i].classList.add("displayblock");
}
return true;
}
function removeClass(a) {
var e = [];
var e = document.getElementsByClassName(a);
console.log(e);
for (i=0;i<= e.length;i++) {
if (!e[i]) return true;
e[i].classList.remove("displayblock");
}
return true;
}
Styles
.list {
display: none;
}
a {
cursor: pointer;
}
.displayblock{
display: block;
}
HTML
<div>
<ul class="list">
<p>sample text</p>
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
</ul>
</div>
<div>
<ul class="list">
<li>list 4</li>
<li>list 5</li>
<li>list 6</li>
</ul>
</div>
<div>
<ul class="list">
<p>sample text</p>
<li>list 7</li>
<li>list 8</li>
<li>list 9</li>
</ul>
</div>
<a onclick="showClass('list');">Find out more</a>
<a onclick="removeClass('list');">Hide more</a>
Share your improve reference
Read the error messsage,
Uncaught ReferenceError: getElementsByClassName is not defined
The actual method you are looking for is window.document.getElementsByClassName

How to check if the element selected/clicked is multiple of "n" using jQuery?

I have 'n' li elements inside a ul. I want to alert a message only if the li selected/clicked is a multiple of "n"(let it be 3).
In the below example, alert must be shown only if I click the 3rd, 6th and 9th li element:
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
<li>list item 7</li>
<li>list item 8</li>
<li>list item 9</li>
</ul>
Here, we can use nth-child as well.
$('ul').find('li:nth-child(3n)').click(function(){
console.log($(this).text());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
<li>list item 7</li>
<li>list item 8</li>
<li>list item 9</li>
</ul>
You can use :nth-of-type() selector
$('li:nth-of-type(3n)').click(function() {
console.log('Alert here')
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
<li>list item 7</li>
<li>list item 8</li>
<li>list item 9</li>
</ul>
You could use the modulo % for this with the help of index() to get the index of the clickd li.
We should add 1 since the index is zero based :
if (($(this).index() + 1) % n == 0) {
//Your logique here
}
Snippet:
var n = $('ul>li').length;
$('li').click(function() {
var currentIndex = $(this).index();
if ( (currentIndex + 1) % n == 0)
{
console.log('Alert here')
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
<li>list item 7</li>
<li>list item 8</li>
<li>list item 9</li>
</ul>
You can try use this:
var number = 3;
$("ul li").click(function(){
var i = $(this).index() + 1
if(i % number == 0) {
console.log("You clicked on either element 3,6 or 9")
}
})
Demo
var number = 3;
$("ul li").click(function(){
var i = $(this).index() + 1
if(i % number == 0) {
console.log("You clicked on either element 3,6 or 9")
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
<li>list item 7</li>
<li>list item 8</li>
<li>list item 9</li>
</ul>
Or you can use $(ul li:nth-child(3n)).click() it will take each third element.
Use jQuery index()
UPDATE : Directly running this code should satisfy your requirement. N=3 in this example.
Here is the jsfiddle.
$(document).ready(function(){
var N = 3;
$("ul li").click(function(){
var index = $(this).index() + 1;
if(index%N == 0) {
alert("Is a multiple of "+N);
} else {
alert("Is not a multiple of "+N);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
<li>list item 7</li>
<li>list item 8</li>
<li>list item 9</li>
</ul>

javascript. swap array elements on click

I git this list of items and an array to hold 4 of them. When clicking on an item i want it to get pushed to the array, but the array should never be more or less than 4 in its length. So when clicking on one of the LI's i want to push that item to the array. and when the array is full (when the lenght is 4) i instead want the first element in the array to be swapped out to whatever next item that gets clicked is.
My markup looks similar to this:
<ul>
<li>val 1</li>
<li>val 2</li>
<li>val 3</li>
<li>val 4</li>
<li>val 5</li>
<li>val 6</li>
<li>val 7</li>
<li>val 8</li>
<li>val 9</li>
</ul>
var ar=[];
function clickFunc(e){
if(ar.length<4){
ar.push(e.innerHTML);
}
else{
ar.unshift(e.innerHTML);
ar.pop();
}
console.log(ar)
}
<ul>
<li onclick="clickFunc(this)">val 1</li>
<li onclick="clickFunc(this)">val 2</li>
<li onclick="clickFunc(this)">val 3</li>
<li onclick="clickFunc(this)">val 4</li>
<li onclick="clickFunc(this)">val 5</li>
<li onclick="clickFunc(this)">val 6</li>
<li onclick="clickFunc(this)">val 7</li>
<li onclick="clickFunc(this)">val 8</li>
<li onclick="clickFunc(this)">val 9</li>
</ul>
u can unshift(),then pop() array.

How to have KendoPanel's all the sections expand

I just started using KendoPanel and I am wanting to expand all the segments of the panel under certain condition. I am using following code to achieve that:
var panelbar = $("#KendoPanel").kendoPanelBar();
var kendoPanelbar = panelbar.data().kendoPanelBar;
kendoPanelbar.collapse($("li", panelbar.element));
Seems like it is contracting all the segments instead of expanding them. What am I doing wrong?
Please try with below code snippet.
<ul id="mypanelbar">
<li class="k-state-active">First Item
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
<li>Second Item
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
<li>Third Item
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
<li>Fourth Item
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
<li>Fifth Item
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
</ul>
<script>
var panelBar;
$(document).ready(function () {
panelBar = $("#mypanelbar").kendoPanelBar().data("kendoPanelBar");
// Expand all item
panelBar.expand($("#mypanelbar li.k-item"));
});
</script>
Let me know if any concern.

Is there anyway to stop collapsible side navigation to stop jumping on page load?

The code works except it keeps jumping every time I load or refresh the page and I was wondering if anyone knows of a good solution to this? Any help much appreciated.
Press the 'Run' button on this Jsfiddle to see what I mean.
<div id="sideNav_header">Navigation</div>
<ul id="collapsibleMenu">
<li>Link
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
<li>Link</li>
<li>Link</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 2.1</li>
<li>List Item 2.2</li>
<li>List Item 2.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 3.1</li>
<li>List Item 3.2</li>
<li>List Item 3.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 4.1</li>
<li>List Item 4.2</li>
<li>List Item 4.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 5.1</li>
<li>List Item 5.2</li>
<li>List Item 5.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 1.1</li>
<li>List Item 1.2</li>
<li>List Item 1.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 2.1</li>
<li>List Item 2.2</li>
<li>List Item 2.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 3.1</li>
<li>List Item 3.2</li>
<li>List Item 3.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 4.1</li>
<li>List Item 4.2</li>
<li>List Item 4.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 5.1</li>
<li>List Item 5.2</li>
<li>List Item 5.3</li>
</ul>
</li>
<li>Link
<ul>
<li>List Item 5.1</li>
<li>List Item 5.2</li>
<li>List Item 5.3</li>
</ul>
</li>
</ul>
//Script//
$("#collapsibleMenu > li > a").find("+ ul").slideUp(1);
// Expand or collapse:
$("#collapsibleMenu > li > a").click(function() {
$(this).find("+ ul").slideToggle("slow");
});​
Yes, change:
$("#collapsibleMenu > li > a").find("+ ul").slideUp(1);
to:
$("#collapsibleMenu > li > a").find("+ ul").slideUp(0);
http://jsfiddle.net/DjbeK/1/
Using slideUp(1) on page load doesn't make sense you can use hide() instead.

Categories

Resources