How do I keep current item in view in an overflown div? - javascript

I have a div with a fixed height of 400px and a long list of items inside. Clicking on prev/next links will move you throught the list. The problem is, after a while, the current item will be out of view. How can I move the scrollbar with the current item?
I've made a demo, have a look: http://jsbin.com/idunaf
<!DOCTYPE html>
<html>
<head>
<style>
#listContainer{height:400px;width:300px;overflow:auto;background-color:#eee;}
.selectedItem{background-color:white;color:red;}
</style>
<script class="jsbin" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
for(var a=[],i=0;i<100;++i){
a[i]=i;
}
currentItem = 0;
$(document).ready(function() {
var items = [];
$.each(a, function(i,x){
items.push('<li id="item_'+x+'">item #'+x+'</li>');
});
$('#list').append(items.join(''));
$('#next').click(function() { updateList(); currentItem += 1; });
$('#prev').click(function() { updateList(); currentItem -= 1; });
});
function updateList(){
$('#listContainer').find('.selectedItem').removeClass('selectedItem');
$('#item_'+currentItem).addClass('selectedItem');
}
</script>
</head>
<body>
<< PREV ITEM | NEXT ITEM >>
<div id="listContainer">
<ul id="list"></ul>
</div>
</body>
</html>

You should be able to use the (non-jQuery) DOM method element.scrollIntoView():
$('#item_'+currentItem).addClass('selectedItem')[0].scrollIntoView(true);
// or
$('#item_'+currentItem)[0].scrollIntoView(true);
// or
document.getElementById('item_'+currentItem).scrollIntoView(true);
The [0] is just to get a reference to the actual DOM element from the jQuery object - given you're selecting by id I assume there can only ever be one element matching the selector. Of course with most jQuery methods setup to allow chaining you can do this on the end of the existing .addClass() rather than selecting the element again.
(Have a look at the .scrollIntoView() doco to decide whether to pass true or false (or nothing) to the method.)

Related

How to built a text array of the document content with javascript

Hi how can I extract the text of an document as an array within javascript.
It´s easy to get the innerHTML, but I do not get the text before and after the div for example.
This should be the output:
[0]=before div
[1]=innerHTML
[2]=aferHTML
[3]=before div2
[4]=innerHTML2
[5]=aferHTML2
Of the following document:
<html><head>
<body>
before div <div>innerHTML </div>aferHTML
before div2 <div>innerHTML2 </div>aferHTML2
</body></html>
I found this link, but it does not get the text before and after the elements as well:
How to get all text from all tags in one array?
You scenario is not clear. Could you please elaborate more the specific reason.
However if you want to get text from all elements in a document, kindly review this thread -> link.
By using the childNodes property you can achieve this. But for afterHtml and before div2, you need to do some extra work because they are part of the same text node.
Please take a look at the snippet below. You can remove the last element of the array manually.
const arr = [];
document.body.childNodes.forEach(node => {
arr.push(node.textContent.trim());
})
console.log(arr)
<body>
before div <div>innerHTML </div>aferHTML
before div2 <div>innerHTML2 </div>aferHTML2
</body>
Okay here's mine.
As you can see, I wrapped your HTML inside a div with a class name content.
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div class="content">
before div <div>innerHTML </div>aferHTML
before div2 <div>innerHTML2 </div>aferHTML2
</div>
</body>
</html>
<script type="text/javascript">
var body = document.querySelector('.content').children;
var list = [];
for (var i = 0; i < body.length ; i++) {
var before = body[i].previousSibling.nodeValue.trim();
var inner = body[i].innerHTML;
var after = body[i].nextSibling.nodeValue.trim();
if (before && i == 0) list.push(before); //prevent duplication and empty value
list.push(inner);
if(after) list.push(after); //prevent empty value
}
console.log(list); //output
</script>
in innerHTML, you could split the string using inner.split(" ") if you like.

How to get the respective HTML element and its index when clicked on a web page?

I have a webpage. Suppose I don't know that the page contains a <p> or any other HTML tag. The script runs in the background and when I click anywhere on the webpage if there is a corresponding HTML tag to the click then the script returns the tag with its index number.
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<p></p>
<div></div>
<div></div>
</body>
</html>
Now when I click on the tag p then the script should return <p> and its index. Somewhat like this:
$("body").click(function(){
/* return the clicked html tag within body and its index*/
});
For this:
$("tag name").click(function(){
// return tag
});
I need to know which tag I am clicking on. But I don't know on which tag I'm going to click.
Pure JS will also do.
Just use event delegation within the on method. http://api.jquery.com/on/
If you provide a selector JQuery will setup the event to fire only when an element within the parent matches that selector. In the below it grabs any element because the selector is "*" - this has the added benefit of providing the current clicked element as this or e.currentTarget within the function call.
$("body").on("click", "*", function(e) { alert(this) });
To get the index of the specified element within the body you can use that this context to grab the index based on its tagName (a.e. paragraphs have a tagName of p)
$("body").on("click", "*", function(e) { alert(this + " " + $(this).index(this.tagName))}
$("body").on("click", "*", function(e) { alert(this + " " + $(this).index(this.tagName)) });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<p>paragraph</p>
<div>div</div>
<div>div</div>
</body>
</html>
p is tag contained with body if u use
$("body").click(function(){
/* return the clicked html tag within body and its index*/
});
so all of child if you clicks will read body click
if u want click p try it
$('p').on('click',function(){});
and dont use
$("body").click(function(){
/* return the clicked html tag within body and its index*/
});
or u can try it for click p
$('body p').on('click',function(){});
The event context returns the html
$('body').click(function(){ var clicked = $(event.target).context; return clicked;})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<body>
<p> Nice Body</p>
<p> No pun intended </p>
</body>
Without jQuery you can attach an onclick handler to your elements like so:
for (let i = 0, len = document.body.children.length; i < len; i++) {
document.body.children[i].onclick = function(){
alert(i) ;
}
}
This will show you the index of the item on the page.
The event object that is passed to the event listener contains the element that triggered the event on its target property. So if you need a reference to the element itself just access that property:
var element = event.target;
If you need the name of the element, for instance P, then access the nodeName property of the element object:
var name = element.nodeName;
Finding the index is just a matter of going through the element's parent's children property and seeing which index that element is at. You can do this easily by turning the children property into an Array and calling indexOf() passing in the element
Array.from(element.parentElement.children).indexOf(element);
Demo (note the index is 1 for the P and 2 and 3 for the div's as <script> is the first element child)
$(document.body).click(function(event) {
var element = event.target;
var elementName = element.nodeName;
var index = Array.from(element.parentElement.children).indexOf(element);
console.log("Element: ", elementName);
console.log("Index: ", index);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>paragraph</p>
<div>div 1</div>
<div>div 2</div>

show() method removes the html element from dom instead of displaying it

I have this code,
HTML and php
<?php for($i = 1; $i <= 5; $i++) { ?>
<div class="file-add-row" style="display:none;">Some content</div>
<?php } ?>
<div id="add-file-plus">Add File</div>
and the JS is
$('#add-file-plus').live('click', function () {
if($('div.file-add-row:visible').length == 0) {
$('div.file-add-row:hidden:first').show();
} else {
$('.file-add-row:hidden:first').removeAttr("style").insertAfter($('.file-add-row:visible:last'));
}
});
Now, my problem is, when I click the add button for the first time, the first 'file-add-row' div is displayed. But when I click the add button the second time, nothing happens on the page. Instead, it just completely removes that div from the dom.
I am just a beginner in jquery, so there might be things I overlooked. Anyone got any idea about what's going on ?
When you do:
$('.file-add-row:visible:last')
Just after:
$('.file-add-row:hidden:first').removeAttr("style")
They both refer to the same object. And if you try to insert an object after itself, it will end up removing itself from the DOM.
Change the JS to:
$('#add-file-plus').live('click', function () {
if($('div.file-add-row:visible').length == 0) {
$('div.file-add-row:hidden:first').show();
} else {
var last_visible = $('.file-add-row:visible:last')
$('.file-add-row:hidden:first').removeAttr("style").insertAfter(last_visible);
}
});
Demo (Click on 'Add File'):
https://jsfiddle.net/woxd2jbf/1/
Here's a version without jQuery just plain JavaScript, it will work with divs just as it does with button, ul, and li. The details are commented within the source.
Key Methods
cloneNode()
appendChild()
PLUNKER
SNIPPET
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!--This <li> is a template to clone-->
<li class="row" style='display:none'>Some content</li>
<!--This is the empty list to be populated with clones-->
<ul id='list'>
</ul>
<!--This button will have an eventListener
that will execute a function when it is clicked-->
<button id="add">Add File</button>
<script>
/* Reference each element involved in process */
// The button
var add = document.getElementById('add');
// The list
var list = document.getElementById('list');
// The first li
var row = document.querySelector('.row:first-of-type');
/*
1. Button will listen for a `click`
2. Create a clone of the first li
3. Add clone as the last child of list
4. Set clone's display property to block
so it's visible
*/
add.addEventListener('click', function(e) {
var clone = row.cloneNode(true);
list.appendChild(clone);
clone.style.display = 'block';
}, false);
</script>
</body>
</html>
$('.file-add-row:hidden:first').removeAttr("style").insertAfter($('.file-add- row:visible:last'));, due to this line the issue is happening. because it first removes the style attribute which makes it visible so $('.file-add- row:visible:last') is returning itself. refactor to store the visible el's reference like below:
$(function () {
$('#add-file-plus').live('click', function () {
if ($('div.file-add-row:visible').length == 0) {
$('div.file-add-row:hidden:first').show();
} else {
var $el = $('.file-add-row:visible:last');
$('.file-add-row:hidden:first').removeAttr("style").insertAfter($el);
}
});
})

If/else JavaScript statement with multiple div traversal

I'm trying to check a set of divs with id='checkbox-selector-chosen' to see if a dynamic variable name exists in those divs. The code below works only if one 'checkbox-selector-chosen' div exists. What I'm trying to do is get the code below to check all of the 'checkbox-selector-chosen' divs for the existence of name.
if($(".checkbox-selector-chosen").text() == name) {
do something...
}
The structure of the divs I'm working with is as follows:
<div id="selected-results" class="group">
<div class="checkbox-selector-chosen">John</div>
<div class="checkbox-selector-chosen">Dave</div>
<div class="checkbox-selector-chosen">Jack</div>
</div>
I'm new to JS/JQuery and I'm sure this is a simple problem, but after 2 days of banging my head against my desk I've decided to ask on here.
Any help will be greatly appreciated!
You may want to use the :contains() selector to see if the item exists (JsFiddle Demo)
var name='Dave';
//Get all items that contain 'Dave'
var items = $(".checkbox-selector-chosen:contains('" + name + "')");
if (items.length > 0) {
alert(items.length + ' items found');
}
UPDATE
If you're worried about having more than one matching item, you can use my example, AND the each() function. (JsFiddle Demo). The Beauty of this approach is that you do not have to loop through all of the child divs - you only need to loop through the divs that contain the desired matching text.
var name='Dave';
//Get all items that contain 'Dave'
var items = $(".checkbox-selector-chosen:contains('" + name + "')");
if (items.length > 0) {
items.each(function(){
if ($(this).html()==name){
alert('item found!');
break;
}
});
}
UPDATE 2
Here is an example which demonstrates the full use of this technique. When you click on a name div, the name is parsed from the object that is clicked and the we use the above method to find the right div. This is, of course, not the best way to accomplish this specific task, but simple illustrates the concept. (JsFiddle Demo)
function findName(name){
var items = $(".checkbox-selector-chosen:contains('" + name + "')");
$('.checkbox-selector-chosen').css({'background':'#fff'});
if (items.length > 0) {
items.each(function(){
if ($(this).text()==name){
$(this).css({'background':'#ff0'});
}
});
}
}
$('.checkbox-selector-chosen').click(function(){
findName($(this).text());
});
Your class selector returns a collection of matches. You could try each() to iterate over them:
$(".checkbox-selector-chosen").each( function() {
if ($(this).text() == name) {
/* ..do something... */
} } );
$('.checkbox-selector-chosen').each(function(){
if($(this).text() == name) {
do something...
}
});
Maybe this is what you're looking for?
ahh beaten to it!
You can user jquery 'each' syntax for this.
$(".checkbox-selector-chosen").each(function(index, value){
console.info("index = "+index +" = "+value);
if(value == name){
console.info("match");
}
});
You want to use jQuery :contains() selector.
<!DOCTYPE html>
<html>
<head>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<meta charset=utf-8 />
<title></title>
</head>
<body>
<div id="selected-results" class="group">
<div class="checkbox-selector-chosen">John</div>
<div class="checkbox-selector-chosen">Dave</div>
<div class="checkbox-selector-chosen">Jack</div>
</div>
<script type="text/javascript">
jQuery(".checkbox-selector-chosen:contains(John)").css("background-color","red")
</script>
</body>
</html>
JSBIN
You could use the contains() selector to filter the results:
$('input[type="checkbox"]').change(function(){
var checked = this.checked;
$('.checkbox-selector-chosen')
.filter(":contains("+this.value+")")
.css('color',function(){
if (checked) return "red";
else return "black";
});
});
example: http://jsfiddle.net/niklasvh/mww3J/

How to Reduce Size of This jQuery Script and Make it More Flexible?

I just created script that shows/hides (toggles) block of HTML. There are four buttons that each can toggle its HTML block. When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button... it hides that HTML block and shows new one.
Here is what I have at the moment:
$('.btn_add_event').click( function() {
$('.block_link, .block_photos, .block_videos').hide();
$('.block_event').toggle();
});
$('.btn_add_link').click( function() {
$('.block_event, .block_photos, .block_videos').hide();
$('.block_link').toggle();
});
$('.btn_add_photos').click( function() {
$('.block_event, .block_link, .block_videos').hide();
$('.block_photos').toggle();
});
$('.btn_add_videos').click( function() {
$('.block_event, .block_link, .block_photos').hide();
$('.block_videos').toggle();
});
Any ideas how to reduce code size? Also, this script isn't very flexible. Imagine to add two new buttons and blocks.
like Sam said, I would use a class that all the blocks share, so you never have to alter that code. Secondly, you can try 'traversing' to the closest block, therefore avoiding it's name. That approach is better than hard coding each specific block, but if the html dom tree changes you will need to refactor. Last, but best, you can pass in the class name desired block as a variable to the function. Below is something you can copy paste that is close to what you started with.
$('.myAddButtonClass').click( function() {
$('.mySharedBlockClass').filter(':visible').hide();
//find a good way to 'traverse' to your desired block, or name it specifically for now.
//$(this).closest(".mySharedBlockClass").show() complete guess
$('.specificBlockClass').show();
});
I kept reading this "When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button" thinking that my eyes were failing me when Its just bad English.
If you want to make it more dynamic, what you can do is add a common class keyword. Then
when the click event is raise. You can have it loop though all the classes that have the
keyword and have it hide them all (except the current one that was clicked) and then show the current one by using the 'this' keyword.
you can refer below link,
http://chandreshmaheshwari.wordpress.com/2011/05/24/show-hide-div-content-using-jquery/
call function showSlidingDiv() onclick event and pass your button class dynamically.
This may be useful.
Thanks.
try this
$('input[type=button]').click( function() {
$('div[class^=block]').hide(); // I resumed html block is div
$(this).toggle();
});
Unfortunatly I couldn't test it, but if I can remember right following should work:
function toogleFunc(clickObject, toogleTarget, hideTarget)
{
$(clickObject).click(function()
{
$(hideTarget).hide();
$(toogleTarget).toggle();
});
}
And the call:
toogleFunc(
".btn_add_videos",
".block_videos",
".block_event, .block_link, .block_photos"
);
and so far
Assuming the buttons will only have one class each, something like this ought to work.
var classNames = [ 'btn_add_event', 'block_link', 'block_photos', 'block_videos' ];
var all = '.' + classNames.join(', .'); // generate a jquery format string for selection
$(all).click( function() {
var j = classNames.length;
while(j--){
if( this.className === classNames[j] ){
var others = classNames.splice(j, 1); // should leave all classes but the one on this button
$('.' + others.join(', .')).hide();
$('.' + classNames[j]).toggle();
}
}
}
All the buttons have the same handler. When the handler fires, it checks the sender for one of the classes in the list. If a class is found, it generates a jquery selection string from the remaining classes and hides them, and toggles the one found. You may have to do some checking to make sure the strings are generating correctly.
It depends by how your HTML is structured.
Supposing you've something like this
<div class="area">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
...
<div class="sender">
<a class="one"></a>
<a class="two"></a>
<a class="three"></a>
</div>
You have a class shared by the sender and the target.
Your js would be like this:
$('.sender > a').click(function() {
var target = $(this).attr('class');
$('.area > .' + target).show().siblings().hide();
});
You show your real target and hide its siblings, which aren't needed.
If you put the class postfixes in an array, you can easily make this code more dynamic. This code assumed that it doesn't matter in which order toggle or hide are called. If it does matter, you can just remember the right classname inside the (inner) loop, and toggle that class after the loop.
The advantage to this approach is that you can extend the array with an exta class without needing to modifying the rest of the code.
var classes = new Array('videos', 'event', 'link', 'photos');
for (var i = 0; i < classes.length; ++i)
{
$('.btn_add_' + classes[i]).click(
function()
{
for (var j = 0; j < classes.length; ++j)
{
if (this.hasClass('btn_add_' + classes[j]))
{
$('.block_' + classes[j]).toggle();
}
else
{
$('.block_' + classes[j]).hide();
}
}
});
}
You could make this code more elegant by not assigning those elements classes like btn_add_event, but give them two classes: btn_add and event, or even resort to giving them id's. My solution is based on your description of your current html.
Here is what I think is a nice flexible and performant function. It assumes you can contain your links and html blocks in a parent, but otherwise it uses closures to precalculate the elements involved, so a click is super-fast.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" ></script>
<script type="text/javascript">
// Enables show/hide functionality on click.
// The elements within 'container' matching the selector 'blocks' are hidden
// When elements within 'container' matching the selector 'clicker' are clicked
// their attribute with the name 'clickerAttr' is appended to the selector
// 'subject' to identify a target, usually one of the 'blocks'. All blocks
// except the target are hidden. The target is shown.
//
// Change clickerAttr from 'linkTarget' to 'id' if you want XHTML compliance
//
// container: grouping of related elements for which to enable this functionality
// clicker: selector to element type that when clicked triggers the show/hide functionality
// clickerAttr: name of the DOM attribute that will be used to adapt the 'subject' selector
// blocks: selector to the html blocks that will be shown or hidden when the clicker is clicked
// subject: root of the selector to be used to identify the one html block to be shown
//
function initToggle(container,clicker,clickerAttr,blocks,subject) {
$(container).each(
function(idx,instance) {
var containerElement = $(instance);
var containedBlocks = containerElement.find(blocks);
containerElement.find(clicker).each(function(idxC, instanceClicker) {
var tgtE = containerElement.find(subject+instanceClicker.getAttribute(clickerAttr));
var clickerBlocks = containedBlocks.not(tgtE);
$(instanceClicker).click(function(event) {
clickerBlocks.hide();
tgtE.toggle();
});
});
// initially cleared
containedBlocks.hide();
}
);
}
$(function() {
initToggle('.toggle','a.link','linkTarget','div.block','div.');
});
</script>
</head>
<body>
Example HTML block toggle:
<div class="toggle">
a <br />
b <br />
c <br />
<div class="A block"> A </div>
<div class="B block"> B </div>
<div class="C block"> C </div>
</div> <!-- toggle -->
This next one is not enabled, to show scoping.
<div class="toggle2">
a <br />
<div class="A block">A</div>
</div> <!-- toggle2 -->
This next one is enabled, to show use in multiple positions on a page, such as in a portlet library.
<div class="toggle">
a <br />
<div class="A block">A</div>
</div> <!-- toggle (2) -->
</body>
</html>

Categories