Scrolling Inside Div Not Working? - javascript

I have a modal which I am using for alphabetical searching. Along the top, there is a horizontal bar with A-Z buttons. I want each of these buttons to scroll the div (with overflow:auto) to the appropriate letter.
Eg if the user clicks "G", it will scroll the div down to the G results.
I would like to set these onclicks with js or jquery. Here's what I have so far:
$('.search__strip__letter').each(function () {
var number = $(this).data('letter');
$(this).click(function () {
var target = '#search__results__letter--'+number;
$('.search-results').animate({scrollTop:$(target).position().top}, 200);
})
})
Why isn't this working...?
EDIT: Here's a fiddle.

Added clearfix class on floated elements, updated jquery selector spelling Fixed here.
$('.search__strip__letter').each(function () {
var number = $(this).data('letter');
$(this).click(function () {
console.log('click');
var target = '#search__results__letter--' + number;
console.log(target,$('.search-results'));
$('.search-results').animate({
scrollTop: $(target).offset().top + 100
}, 200);
})
})
http://jsfiddle.net/Aerious/z17nyh2s/8/

UPDATE
You should be using $(target).offset().top, it works independent of the style's position property.
You must scrollTop to 0 and subtract the .search-results offset's top, also.
UPDATE II
If you want to animate for both sides, instead of scrollingTop to 0, you must get the current scrollTop and sum it, take a look below:
[].forEach.call('ABCDEFGHIJKLMNOPQRSTUVWXYZ', function(l, i) {
var a = document.createElement('a');
a.textContent = l;
a.addEventListener('click', function(e) {
var target = '#search__results__letter--' + i;
var now = $('.search-results').scrollTop();
$('.search-results').animate({ scrollTop: $(target).offset().top - $('.search-results').offset().top + now }, 200);
});
document.getElementById('letters').appendChild(a);
var div = document.createElement('div');
div.id = 'search__results__letter--' + i;
div.textContent = 'Letter ' + l;
document.querySelector('.search-results').appendChild(div);
});
#letters a {
display: inline-block;
width: 25px;
height: 25px;
background-color: darkblue;
margin: 2px;
line-height: 25px;
text-align: center;
color: white;
font-family: Verdana;
cursor: pointer;
}
.search-results {
height: 120px;
overflow-y: auto;
}
.search-results div {
height: 100px;
background-color: aqua;
margin: 2px;
font-family: Verdana;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="letters">
</div>
<div class="search-results">
</div>

I suggest you use the jquery.scrollTo plugin. It's highly customizable, supports animations and is very easy to use. You just need to replace your animate code with something like this:
$('.search-results').scrollTo($(target), 200);

You need a couple modifications to your code to make it work as I understand it. First, you need to give the .search-results element some context. You do this by adding position:relative to that element. Now that it's the relative parent of your search__results__letter--* elements, you can scroll to them with your animate snippet. However, you also need to calculate the scroll position relative to the offset scroll of the parent. Simple math will handle that part by first getting the scroll position of the search__results__letter--* element and adding it to the scrollTop() of .search-results. You can see this method in the demo code below:
$('.search__strip__letter').each(function () {
var number = $(this).data('letter');
$(this).on('click', function () {
var target = '#search__results__letter--'+number;
var currentPosition = $('.search-results').scrollTop();
$('.search-results').animate({scrollTop:currentPosition + $(target).position().top}, 200);
})
})
.search-results {
border: 1px solid;
height: 100px;
overflow: auto;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
<li>F</li>
</ul>
<div class="search-results">
<p id="search__results__letter--A">Search Results A</p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p id="search__results__letter--B">Search Results B</p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p id="search__results__letter--C">Search Results C</p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p id="search__results__letter--D">Search Results D</p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p id="search__results__letter--E">Search Results E</p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p id="search__results__letter--F">Search Results F</p>
</div>
EDIT:
I've adapted the original poster's fiddle using my code. One addition I had to make was to force the .search__results__letter back into flow by making a new block formatting context on that class. I did this by adding display:inline-block;width:100%; Adding overflow:hidden; or overflow:auto; should also accomplish the same thing. Also, see MDN docs on block formatting context for other methods to force it.
https://jsfiddle.net/gLpvzgu5/

Related

jQuery .append() and .remove() skipping behavior with slider function

I'm having trouble with some dynamic HTML. I've got a slider that adds or removes DOM elements as the value changes. Each time it increases, an element is added, and each time it decreases, an element is removed.
Here's the HTML:
<input type="range" min="3" max="16" class="rgb-slider" value="3" tabindex="-1" oninput="slider(this.value)">
<div class="container">
<div class="boxes">
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
</div>
</div>
Here's the JS:
var colorCount = 3;
function slider(value) {
if (colorCount < parseInt(value)) {
$('.boxes').append('<div class="box"><span></span></div>');
colorCount = value;
} else {
$('.box:last-child').remove();
colorCount = value;
}
}
http://jsfiddle.net/meu9carx/
However, when I quickly move the slider, it seems to skip or trip up, and I end up with more or fewer than I started with. The slider has a range from 3-16, but sometimes the min value goes to more or less than 3. Sometimes, all the boxes vanish.
Is there a smarter way to code this? I'm trying to avoid hard-coding divs here.
If the mouse moves fast, it's possible for the input value to change by more than one (in either direction) during a single input event. Use the value in the input to determine how many squares there should be exactly, rather than adding or removing only a single element each time.
const boxes = $('.boxes');
$('input').on('input', function() {
const targetSquares = Number(this.value);
while (boxes.children().length < targetSquares) {
boxes.append('<div class="box"><span></span></div>');
}
while (boxes.children().length > targetSquares) {
$('.box:last-child').remove();
}
});
body{
background: #777;
font-family: 'Arimo', sans-serif;
}
.container { padding: 20px 0; }
.boxes { display: flex; }
.box {
padding: 10px;
background: #ffffff;
height: 100px;
flex: 1;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="range" min="3" max="16" class="rgb-slider" value="3" tabindex="-1">
<div class="container">
<div class="boxes">
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
</div>
</div>

Two Column Accordion with Separate Full Width Divs

The intension is to have a two column accordion, without limiting the "expand" field to the left or right column. The catch is that there will be multiple on one page. This is already created, but only button 1 is working. With the way my JS is going, it will get very very repetitive - I am looking for assistance with re-writing the JS to be multiple click friendly. Fiddle: https://codepen.io/ttattini/pen/abLzaaY
EDIT: It would also be perfect if one dropdown would close as the next is opened
HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="row">
<div id="column">
<button id="button">I am Button #1</button>
<button id="button">I am Button #3</button>
</div>
<div id="column">
<button id="button">I am Button #2</button>
<button id="button">I am Button #4</button>
</div>
</div>
<div id="hidden">
<p id="content"> So here I am #1</p>
</div>
<div id="hidden">
<p id="content"> So here I am #2</p>
</div>
<div id="hidden">
<p id="content"> So here I am #3</p>
</div>
<div id="hidden">
<p id="content"> So here I am #4</p>
</div>
CSS
#hidden {
background: #ccc;
margin-top: 2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
#button {
padding: 10px;
margin-top: 5px;
width:50%;
margin-left: 10%;
cursor: pointer;
}
#row {
display: flex;
}
#column {
flex: 50%;
}
JS
$(function() {
var b = $("#button");
var w = $("#hidden");
var l = $("#content");
b.click(function() {
if (w.hasClass('open')) {
w.removeClass('open');
w.height(0);
} else {
w.addClass('open');
w.height(l.outerHeight(true));
}
});
});
The biggest issue is that you're using IDs when you should be using classes. IDs must be unique to each element in a page. When you repeat an ID, JS will only target the first element using that ID. That's why only the first one is working.
The second issue is that, because of the way the script is written, it will only target a single element. What you need to do is get all the elements you want to target by something like their class name and then loop through them, applying the event listener to each one and its appropriate children.
EDIT: Here is an example from some code I wrote for a page with multiple accordions a few weeks ago in vanilla JS
//Below I establish a counting variable and find all the accordions on the page
const acc = document.getElementsByClassName( 'accordion' );
let i;
//Looping through each accordion
for ( i = 1; i <= acc.length; i++ ) {
//Identify target for the event listener. In this case, a heading for each accordion, which I've numbered e.g. "title-1"
const title = 'title-' + i;
const label = document.getElementById( title );
//Identify target content, in this case a list that has a unique ID e.g. "list-1"
const listNum = 'list-' + i;
const list = document.getElementById( listNum );
//Add event listener to heading that toggles the active classes
label.addEventListener( 'click', function() {
label.classList.toggle( 'accordion--active' );
});
}
Of course, there's more than one way to skin a cat, but this is a working example.
I have tracked the clicked event of each button and showed the corresponding hidden content with the use of data- attribute.
I have used vanilla JavaScipt instead of jQuery.
const buttons = document.querySelectorAll('.button');
const hiddens = document.querySelectorAll('.hidden');
buttons.forEach((btn) => {
btn.addEventListener('click', btnClicked)
function btnClicked(e) {
hiddens.forEach((hidden) => {
if(e.target.dataset.btn == hidden.dataset.content) {
hidden.classList.toggle('height')
} else {
hidden.classList.remove('height')
}
})
}
})
.hidden {
background: #ccc;
margin-top: 2%;
padding-left:2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
.hidden.height {
height: 50px;
}
.button {
padding: 10px;
color: white;
background-color: #2da6b5;
border: none;
margin-top: 5px;
width:90%;
margin-left: 5%;
cursor: pointer;
}
.button:hover {
filter: brightness(.9);
}
#row {
display: flex;
}
.column {
flex: 50%;
}
<div id="row">
<div class="column">
<button class="button" data-btn="one">I am Button #1</button>
<button class="button" data-btn="three">I am Button #3</button>
</div>
<div class="column">
<button class="button" data-btn="two">I am Button #2</button>
<button class="button" data-btn="four">I am Button #4</button>
</div>
</div>
<div class="hidden" data-content="one">
<p class="content"> So here I am #1</p>
</div>
<div class="hidden" data-content="two">
<p class="content"> So here I am #2</p>
</div>
<div class="hidden" data-content="three">
<p class="content"> So here I am #3</p>
</div>
<div class="hidden" data-content="four">
<p class="content"> So here I am #4</p>
</div>
Also, please do not use the same ID at multiple elements.

Add a div below inline-block wrapped row - Part 2

A solution suggested by #musicnothing in an older thread displays a content div below the row of inline divs, this works good when the div.wrapblock is clicked itself.
http://jsfiddle.net/SYJaj/7/
function placeAfter($block) {
$block.after($('#content'));
}
$('.wrapblock').click(function() {
$('#content').css('display','inline-block');
var top = $(this).offset().top;
var $blocks = $(this).nextAll('.wrapblock');
if ($blocks.length == 0) {
placeAfter($(this));
return false;
}
$blocks.each(function(i, j) {
if($(this).offset().top != top) {
placeAfter($(this).prev('.wrapblock'));
return false;
} else if ((i + 1) == $blocks.length) {
placeAfter($(this));
return false;
}
});
});
The issue I'm having.
I need to trigger the same effect, but by adding the click event to a link within the wrapblock itself.
My code is nearly identical.
What I have changed is the click event handle, from $('.wrapblock').click(function() to $('.more').on('click', function() I also needed to add .closest(".wrapblock") for the content div to position itself outside of the wrapblock.
$('.more').on('click', function() {
...
if ($blocks.length == 0) {
placeAfter($(this).closest(".wrapblock"));
return false;
}
Everything can be seen and tested http://jsfiddle.net/7Lt1hnaL/
Would be great if somebody could shed some light on how I can calculate which block it needs to follow with the offset method, thanks in advance.
As you can see in the latest fiddle example, the content div is not displaying below the row of divs.
I also apologise, I wanted to post on the thread in discussion but I only have a minor posting reputation which doesn't let me, thanks.
var $chosen = null;
var $allBlocks = [];
$(function(){
$allBlocks = $('.wrapblock');
})
$(window).on('resize', function() {
if ($chosen != null) {
$('#content').css('display','none');
$('body').append($('#content'));
$chosen.trigger('click');
}
});
$('.more').on('click', function() {
$chosen = $(this);
var position = $chosen.parent('.wrapblock').position();
$('#content').css('display','inline-block');
$allBlocks.filter(function(idx, ele){
return $(ele).position().top == position.top;
})
.last()
.after($('#content'));
});
.wrapblock
{
background: #963a3a;
display: inline-block;
width: 90px;
height: 90px;
color: white;
font-size: 14px;
text-align: left;
padding: 10px;
margin: 10px;
vertical-align:top;
position:relative;
}
#content
{
display:none;
vertical-align:top;
width:100%;
background: #5582c1;
font-size: 12px;
color: white;
padding: 10px;
}
.more {
position:absolute;
bottom:15px;
right:15px;
cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div class="wrapblock">1
<span class="more" data-ref="1">more</span>
</div>
<div class="wrapblock">2
<span class="more" data-ref="2">more</span>
</div>
<div class="wrapblock">3
<span class="more" data-ref="3">more</span>
</div>
<div class="wrapblock">4
<span class="more" data-ref="4">more</span>
</div>
<div class="wrapblock">5
<span class="more" data-ref="5">more</span>
</div>
<div class="wrapblock">6
<span class="more" data-ref="6">more</span>
</div>
<div class="wrapblock">7
<span class="more" data-ref="7">more</span>
</div>
<div class="wrapblock">8
<span class="more" data-ref="8">more</span>
</div>
<div class="wrapblock">9
<span class="more" data-ref="9">more</span>
</div>
<div id="content">Some Content</div>
Seems to do what you want. Basically, it just filters down the set of all blocks to the row of the block you clicked on using the assumption that they'll all have the same vertical offset (top), then takes the last one, because jQuery will keep them in document order, so that'll be the last one in the layout row.
Oh, and I updated the fiddle http://jsfiddle.net/7Lt1hnaL/1/

Get input value and generate multiple textarea and set value there

I have a DOM like this, when i fill the input field and click the button i need to create a textarea element and and stored the input value there.
if i click multiple times create multiple textarea and multiple ID's, How can i do this please check my code, Best answers must be appreciated
$('#note').on('click', function(){
var storedNoteVal = $('#enterVal').val();
var count_id = 1;
var noteCov = $('.note_cover');
$('#content_bag').prepend('<div class="full-width note_cover" id="noteId"><textarea></textarea></div>');
$(noteCov).find('textarea').val(storedNoteVal);
$(noteCov).each(function(index, element) {
$(this).attr('id', 'noteId' + count_id);
count_id++;
});
});
.full-width.note_cover {
float: left;
margin-bottom:15px;
}
.note_cover textarea {
height: auto !important;
height: 45px !important;
resize: none;
width: 100%;
/*border:none;*/
}
<div class="col-md-11 col-md-offset-1 col-sm-8 col-xs-12 mtp" id="content_bag">
</div><!-- #content_bag -->
<input type="text" placeholder="Enter project Tags" class="majorInp" id="enterVal" />
<button id="note">click me</button>
Your code is working fine, just put storedNoteVal in text-area, and input won't generate any text-area if its blank.
$('#note').on('click', function() {
var storedNoteVal = $('#enterVal').val();
var count_id = 1;
var noteCov = $('.note_cover');
if(storedNoteVal){
$('#content_bag').prepend('<div class="full-width note_cover" id="noteId"><textarea>' + storedNoteVal + '</textarea></div>');
//$(noteCov).find('textarea').val(storedNoteVal);
$(noteCov).each(function(index, element) {
$(this).attr('id', 'noteId' + count_id);
count_id++;
});
}
});
.full-width.note_cover {
float: left;
margin-bottom: 15px;
}
.note_cover textarea {
height: auto !important;
height: 45px !important;
resize: none;
width: 100%;
/*border:none;*/
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="col-md-11 col-md-offset-1 col-sm-8 col-xs-12 mtp" id="content_bag">
</div>
<!-- #content_bag -->
<div>
<input type="text" placeholder="Enter project Tags" class="majorInp" id="enterVal" />
<button id="note">click me</button>
</div>
Building on Abhinshek answer -
Your code actually reassign id's to the textareas, since you loop through all the elements after prepending them.
You could define count_id as a window variable (outside the click function) and then just use it.
Also, you don't need to wrap noteCov with $() since $('.note_cover') returns a jQuery objects array
var count_id = 1;
$('#note').on('click', function() {
var storedNoteVal = $('#enterVal').val();
$('#content_bag').prepend('<div class="full-width note_cover" id="noteId_'+count_id+'"><textarea>' + storedNoteVal + '</textarea></div>');
count_id++;
});
This way each textarea gets it's own unique id that doesn't change

HTML JS form to div comments (temporary change)

I have seen many similar problems but when I try them they end up failing. It has gotten to the point where my code is totally messed up and I need some help both cleaning it up and fixing my issue. (using chrome)
So far I have tried selecting the value of the form and putting that into a div,
I have tried to use the button as just a link to start the script so that the page doesn't reset and also many other answers found on-line, none of them are helping so I am asking for a personalised help.
function on_comment_add() {
var main = document.getElementById("div1");
var add_user_name = document.createElement("div");
var add_user_comment = document.createElement("div");
add_user_name.setAttribute("id", "add_user_name");
add_user_comment.setAttribute("id", "add_user_comment");
<!-- var node = document.createTextNode("This is new."); -->
var node_1 = document.getElementById("user_name").value;
var node_2 = document.getElementById("user_comment").value;
add_user_name.appendChild(node_1);
add_user_comment.appendChild(node_2);
var element = document.createElement("div");
element.setAttribute("id", "display_comment_div");
element.appendChild(add_user_name);
element.appendChild(add_user_comment);
main.appendChild(element);
main.innerHTML = element;
return false;
}
body {
background-color: lightGreen;
}
div.middle {
width: 80%;
margin-left: 10%;
background-color: #47e077;
height: 940px;
font-size: 10pt;
font-family: aubrey;
border: 3px solid gold;
}
.comments-form {
text-align: center;
}
#display_comment_div {
background: rgba(200, 54, 54, 0.1);
width: 80%;
margin-left: 9%;
border: 0.1px solid lightGreen;
border-radius: 25px;
}
#add_user_name {
width: 45%;
float: left;
}
#add_user_comment {
width: 45%;
display: inline-block;
float: right;
}
<div class="middle">
<div class="comments-form">
<form>
<label for="name" style="width:100px; display:inline-block;">Name</label>
<input id="user_name" type="text" placeholder="name goes here" style="width:300px; margin-left:5px;" />
<br><br>
<label for="comment" style="width:100px; display:inline-block;">Comment</label>
<textarea id="user_comment" placeholder="comment goes here" maxlength="150" style="width:300px;max-width:300px;"></textarea><br>
<button style="margin-left:310px;" onmousedown="return on_comment_add">Submit</button>
</form>
<div id="div1">
</div>
</div>
</div>
I guess what I am asking is if anyone can help me display the username and comment below the form but it seems tricky for me because I have gone through so many answers that don't work for me that I cannot think of any other ways to do it.
For clarification this code is not meant to keep the comments from the form nor is it meant to be a fully functioning site. I am just making slight modifications to some code so that I can hand it in as a college assignment.
Using onclick and pass the event inside:
<button style="margin-left:310px;" onclick="on_comment_add(event)">Submit</button>
And disable the default form submit action:
function on_comment_add(e) {
e.preventDefault()
var main = document.getElementById("div1");
var add_user_name = document.createElement("div");
var add_user_comment = document.createElement("div");
add_user_name.setAttribute("id", "add_user_name");
add_user_comment.setAttribute("id", "add_user_comment");
var node_1 = document.createElement("div");
node_1.innerHTML= document.getElementById("user_name").value;
var node_2 = document.createElement("div");
node_2.innerHTML = document.getElementById("user_comment").value;
add_user_name.appendChild(node_1);
add_user_comment.appendChild(node_2);
var element = document.createElement("div");
element.setAttribute("id", "display_comment_div");
element.appendChild(add_user_name);
element.appendChild(add_user_comment);
main.appendChild(element);
return false;
}
Workable example: https://jsfiddle.net/kingychiu/z6gnqswn/
Change type to "button" to prevent automatical form sending and add parentheses to onmousedown expression:
<button type="button" style="margin-left:310px;" onmousedown="return on_comment_add()">Submit</button>
Then change this
add_user_name.appendChild(node_1);
add_user_comment.appendChild(node_2);
to this (since node_1, node_2 are values, not elements):
add_user_name.innerHTML = node_1;
add_user_comment.innerHTML = node_2;
And remove that line
main.innerHTML = element;
above
return false;
That should work.

Categories