When I do sites for clients I constantly get the request to have a two sided layout, where buttons on the left cause divs on the right to fade in and fade out appropriately. For a long while I've been writing each case. (which is a bit silly and time consuming). I want to systematize that, but I've been having a hard time since I don't understand how javascript/jquery works with .click() and arrays.
$(document).ready(function(){
var left = new Array($('#a'),$('#b'), $('#c'),$('#d'), $('#e'));
var right = new Array($('#1'),$('#2'),$('#3'),$('#4'),$('#5'));
// left[0].click(function(){right[0].fadeIn('fast');})
var numbers = new Array(1,2,3,4,5);
function fadey(x){
left[x].click(function(){
right[x].fadeIn('fast');
})
}
for (var i = 0; i < left.length; i++) {
fadey(i);
};
})
This code gets it so that the left hand side buttons cause the corresponding right hand div to appear. The problem is that I can't seem to get the other divs to disappear without causing all the divs to disappear.
Ideally it would be dope if I could have one line of code that simply checks when the left-hand jquery object array is clicked, gets its index value, and causes the corresponding right hand side jquery object array to appear, while also hiding the other ones.
This way I can just plug elements into each array and then never have to worry about writing these cases one by one.
Thank you so much for your help!
You could just make a generic event handler with classes instead of IDs:
$('.left button').click(function() {
var index = $(this).index();
$('.right section').eq(index).fadeIn('fast');
});
I'm assuming your HTML looks something like this:
<aside class="left">
<button>One</button>
<button>Two</button>
<button>Three</button>
</aside>
<aside class="right">
<section>One</section>
<section>Two</section>
<section>Three</section>
</aside>
Related
I have a quiz Django app which consists of two parts. One is showing 10 sentences with their audio to remember, one per page, and the second is asking questions for the same set of sentences. First part was set up with js function which creates pagination in my html the following way:
my_template.html
<button id="prev">prev</button>
<button id="next">next</button>
<ul class="list-articles" id="dd">
</ul>
<script>
var my_objects = `{{ my_objects|safe}}:` #list of items from my view function
function paginate(action) {
console.log(action)
if (action ==='next') {
page_number++;
}
else{
page_number--;
}
const audio = document.createElement('audio');
audio.src =`/media/${my_objects[page_number].fields.audio}`;
$('#dd').empty();
$('#dd').append('<li><h1>'+ my_objects[page_number].fields['content'] +'</h1></li>');
$('#dd').append(audio);
$('#page_number').val(page_number);
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#next').onclick = function() {
paginate('next'); #same goes for 'prev' button.
}
})
</script>
Now when the user paginated through all the sentences I want to show the continue button and if the user clicks it, start the second part. The second part has absolutely same page except I need to hide content of my objects and leave only audio or vice versa and add textarea tag for the user's input. After that I need to loop over my objects again - now in the form of questions. I need to do that without page re-rendering so I don't need to store the list of objects or query the DB again.
I tried to make it with tag disabling and activating the second loop after the first ends but that looks quite messy and I suppose there is a smarter way of doing that. I'm not a JS developer so any help would be appreciated!
Thanks for all who viewed and not commented! I found the answer myself. Just need to add JS functions which will empty the main tag and refill with necessary field.
Details: By accessing elements of DOM in the following way you can empty any element on a web page. For example, if we have a div like:
<div class= id="maindiv">
<h2> Hello, world! </h2>
</div>
We can empty and refill it with a new header:
var container = document.getElementById("maindiv");
container.empty();
new_header = document.createElement('h2');
new_header.innerHTML = 'Hello, brave new world!'
container.append(new_header);
Same applies to the whole web-page. The only thing is, if it is too big, you probably going to be tired of manipulating the DOM each time. So, you may want to check out some frameworks like React to make it easier .
I'm looking to draw a 3D cylinder with javascript by copying the layers and applying an increased margin to these elements. I have tried to set the height of the element in my input and run the copy function while total margin of the copied elements is lower than the set height of elements.
http://jsfiddle.net/yuX7Y/3/
<form>
<input type="number" id="userHeight" />
<button type="submit" onclick="circleHeight">Submit</button>
</form>
<div class="circle">
</div>
<div class="slice">
</div>
$(document).ready(function(){
var initMargin = 4;
var totalMargin = 0;
var i = 0;
function copy(){
$(".slice").clone().appendTo( ".content" ).css({'margin-top': totalMargin + "px"});
console.log("cloned");
i++;
totalMargin = initMargin + 4;
}
function setH(){
while(i < (document.getElementById("userHeight").value)){
copy();
}
if(i>100){
initMargin = 4;
i=0;
}
}
});
Jump To The Result: http://jsfiddle.net/yuX7Y/15/
Notes
This Fiddle/question intrigued me so I went ahead and looked at it for a little while. There were actually a number of issues, some obvious and some less obvious. Somewhat in the order I noticed them these are some of the issues:
jQuery wasn't included in the fiddle
The click event wasn't wired up correctly - it was actually trying to submit the form. You need to use e.preventDefault to stop the form from submitting. Since you were already using jQuery I just wired up with the jQuery click event:
$("#recalculateHeight").click(function (e) {
e.preventDefault();
setH();
});
Because of the use of "global" variables (variables not initialized within the routines), the submit would only work once. Instead of this, I moved variable declarations to the appropriate routine.
Calling $(".slice").clone() clones ALL slice elements on the page. The first time you clone, this is fine. But after that you are cloning two elements, then three elements, etc (as many slice elements as are on the page). To solve this I created a slice template like:
<div class="slice" id="slice-template" style="display: none"></div>
Then you can clone to your hearts content like $("#slice-template").clone(). Just don't forget to call the jQuery show() method or set display back to block on the cloned element.
Finally, if you want to repeat the process many times you need to be able to clear previous elements from the page. I find this easiest to do by creating containers, then clearing the contents of the container. So I put all of the "slices" into this container:
<div class="content">
Now when you want to clear the content node you can just call $(".content").empty();.
I also made a few style based changes in my Fiddle, but those don't make it work or not work, they just help me read the code! So, there you have it! Best of luck!
I need to create a nested drag/drop functionality using purely Javascript (No Jquery or other plugins please).
The Idea is to have a several div tags as groups and having ability to drag that div tag/group on top of another div tag/group to create a sub group within itself(as a child of that group) max level of sub subs allowed is 4. To Illustrate what I am talking about please look at this Jquery Plugin NestedSortabled example, It defines exactly what I am looking for.
NestedSortable Jquery Example
Another similar example: http://dbushell.com/2012/06/17/nestable-jquery-plugin/
I need to develop my code to function exactly like the example above, but using purely old school javascript only, please dont suggest any Jquery code.
Here is what I have currently working, However I am stuck right now and cant figure out how to get the sub grouping functionality to work. Please Help!!
My working Demo: http://jsbin.com/IzAfutI/1
My working Demo + Code: http://jsbin.com/IzAfutI/3/edit?html,css,js,output
Edit:
Let me example the code in more detail. StartDrag and StopDrag contain the main logic behind the functionality. Basically when user drags a div tag I am currently creating a container on top of or underneath a already existing div tag for the item that is to be dragged to be placed into, however when I use this same funcionality to create that container within another container(via creating sub group) I am getting an error. which means Maybe I am going at the problem the wrong way maybe my logic might be wrong or else something else wrong with the code.
HTML mark up of group div tag:
<div class="dragContainerUsed">
<div id="a7b94a42-fb00-4011-bd5a-4b48e6e578c5" class="dragPanel">
<input type="hidden" value="1|fa7989d7-1708-4a90-9bf6-c91f6cef6952" />
<div onmousedown="startDrag(event, this.parentNode)" class="dragPanelHeader">
<div style="margin-left:4px; margin-top:3px; float:left;">1 - Group 1<span id="gta7b94a42-fb00-4011-bd5a-4b48e6e578c5"></span></div>
</div>
</div>
<div class=\"dragSubContainerUnUsed\"></div>
</div>
<div class="dragContainerUnUsed"></div>
So what I want to happen is when user drags another div on top of the div dragSubContainerUnUsed it should be placed within that subContainer....
On start drag, I create a array to store all the containers and subContainers:
containers = new Array();
subContainers = new Array();
containers.push(dragTarget);
for (i = 0; i < divs.length; i++) {
if (divs[i].className.toLowerCase() == "dragcontainerunused") {
containers.push(divs[i]);
}
}
for (i = 0; i < divs.length; i++) {
if (divs[i].className.toLowerCase() == "dragSubcontainerunused") {
subContainers.push(divs[i]);
}
}
and currently the part where I am stuck is in the functions onDrag and stopDrag, I dont know how to get the subContainers to work via to create the subgroups...
For Instance if I drag Group 3 on top of Group 2, I want group 3 to be a sub group of 2 Like this:
and I should be able to add max of 4 groups into each sub group, with max of 4 sub groups.
like this:
and finally there should only be a max of 4 levels of subgrouping
like this:
Please Help in anyway you can, if you can identify the problem than please tell me or if there needs to be a change in logic for my code tell me, Even if you can completely re-write/ create your own new code to make this application work would be very much appreciated. I have been trying to tackle this for a few days any and all help will be greatly accepted...
Check This code
function allowDrop(ev){
ev.preventDefault();
}
function drag(ev)
{
ev.dataTransfer.setData("Text",ev.target.id);
}
function drop(ev)
{
ev.preventDefault();
var data=ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
}
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
</div>"
<img id="drag1" src="img_logo.gif" draggable="true" ondragstart="drag(event)" width="336" height="69">`
Been working on this, completed it couple of months ago. but forgot about this post. so i decided to post the complete solution for anyone else that might be looking for a answer to this enjoy.
The entire code is too long to post it in here, but here is the link to the code:
Drag/Drop Functionality with Div Tags
DEMO: http://jsbin.com/aTUHULu/1
You have to divide the drop rectangle into 4 areas , if you drop it in the first one it will be on level 1 and if you drop it on the next 40px lets say it will be considered level 2 and hence
I would post the code as well if you want but I think the approach itself would work if you try and implement it. Here is the approach
Instead of div, add the item as a listitem . This would auto intend the control and it would be very easy for you to parse it at the end. What you need to do here is that onDrop, take the text (and other properties) from the div, create a li and add the div in that li. This would enable your code to redrag and drop the div further. Note - remember to remove the li (or ul according to the parent of div) when drag ends.
I want to swap two html div tags entirely, tags and all. I tried the code below code but it does not work.
jQuery('#AllBlock-'+Id).insertAfter('#AllBlock-'+Id.next().next());
How to swap two div tags entirely.
You have some bracket mismatching in your code, it looks like you might be trying to do this:
jQuery('#AllBlock-'+Id).insertAfter($('#AllBlock-'+Id').next().next());
Which would take something like:
<div id="AllBlock-5"></div>
<div id="AllBlock-6"></div>
<div id="AllBlock-7"></div>
And, if called with Id 5, turn it into this:
<div id="AllBlock-6"></div>
<div id="AllBlock-7"></div>
<div id="AllBlock-5"></div>
This is because you're taking block 5, and moving it (using insertAfter) to the place after the block that's next().next() (or next-but-one) from itself, which would be block 7.
If you want to always swap #AllBlock-Id with #AllBlock-[Id+2], so they switch places and end up like the following:
<div id="AllBlock-7"></div>
<div id="AllBlock-6"></div>
<div id="AllBlock-5"></div>
You might want to try:
var $block = jQuery('#AllBlock-'+Id);
var $pivot = $block.next();
var $blockToSwap = $pivot.next();
$blockToSwap.insertBefore($pivot);
$block.insertAfter($pivot);
You can't do this because you can't concatenate a string and a jQuery object.
Try this:
var div = $('#AllBlock-'+Id);
div.insertAfter(div.next().next());
it should be like this
you should close the bracket after Id,
jQuery('#AllBlock-'+Id).insertAfter('#AllBlock-'+Id).next().next());
You'll need to detach the existing dom object first, then re-use it later:
$('#divid').detach().insertAfter('#someotherdivid');
What I understand is you want to swap a div when clicked with the last div. What will you do if it is the last div? move it to the top?
This solution should solve the problem, furthermore, you can modify this regex to match the format of your ID. This can probably be made more concise and robust. For example, you could get the last ID a bit more sophisticatedly. This may just be modifying the selector or something more. I mean, you do not want to go rearranging the footer or something just because its the last div on the page.
$('div').click(function() {
//set regex
var re = /(^\w+-)(\d+)$/i;
//get attr broken into parts
var str = $(this).attr('id').match(re)[1],
id = $(this).attr('id').match(re)[2];
//get div count and bulid last id
var lastStr = $('div:last').attr('id').match(re)[1],
lastID = $('div:last').attr('id').match(re)[2];
//if we have any div but the last, swap it with the end
if ( id !== lastID ) {
$(this).insertAfter('#'+lastStr+lastID);
}
//otherwise, move the last one to the top of the stack
else {
$(this).insertBefore('div:first');
} });
Check out this working fiddle: http://jsfiddle.net/sQYhD/
You may also be interested in the jquery-ui library: http://jqueryui.com/demos/sortable/
I am relatively new at javascript, and found an interesting behavior that I can't explain today. I have a custom <hr> (with an image) on a website, which displays oddly in IE7 and below. To overcome this, I wanted to use replaceChild() in combination with getElementsByTag(). Initially, I simply tried to loop over the list, so:
var hrules = document.getElementsByTagName('hr');
for (var i=0; i < hrules.length; i++) {
var newHrule = document.createElement("div");
newHrule.className = 'myHr';
hrules[i].parentNode.replaceChild(newHrule, hrules[i]);
document.write(i);
}
However, this does not work: it actually only gets half the elements, skipping every other one. Printing i gives half-integer values of the actual number of <hr> elements in the document (e.g. if there are 7 <hr/> elements, it prints 4. By contrast, the following does work:
var hrules = document.getElementsByTagName('hr');
var i = 0;
while (i < hrules.length) {
var newHrule = document.createElement("div");
newHrule.className = 'myHr';
hrules[i].parentNode.replaceChild(newHrule, hrules[i]);
document.write(i);
}
i is printed the same number of times as there are hrules in the document (but of course is always 0, since I'm not incrementing it), and the hrules are replaced correctly. I recognize that the while here might as well be while(true)--it's just going until it runs out of <hr> elements, but appears to stop after that (it's not printing any more 0s).
I've tried this with a number of different types of elements, and observed that this only occurs when replacing one kind of element with another. I.e., replacing p with div, span with p, etc. If I replace p with p, div with div, etc. the original example works correctly.
Nothing in the documentation I've found (w3schools, various Google search, here, etc.) suggests an obvious answer.
What is going on here? First, why does the second example I offered work - is replaceChild() iterating over the elements automatically? Second, why is the behavior different for different types of element?
document.getElementsByTagName is a live access to all the HR elements in the document - it's updated whenever you change the document. You don't get a snapshot of all the HRs in the document whenever you call it.
So, with the first code, you are both incrementing i and reducing the size of hrules.length each time round the loop. This explains why you only see half the steps you expect.
Here's the solution I ended up using, in case anyone else (like #Pav above) is curious.
var hrules = document.getElementsByTagName('hr');
/* Each repetition will delete an element from the list */
while (hrules.length) {
var newHrule = document.createElement("div");
newHrule.className = 'ieHr';
/* Each iteration, change the first element in the list to a div
* (which will remove it from the list and thereby advance the "head"
* position forward. */
hrules[0].parentNode.replaceChild(newHrule, hrules[0]);
}
Essentially, what happens is you get a list of all the hrules in the document. This list is dynamically updated as you interact with it (see Matthew Wilson's answer). Each time you change the first element of the list to a div, it gets removed from the list, and the list is updated accordingly. The result is that you simply need to act on the first element of the list each time until the length of the list is 0.
That's admittedly a little counterintuitive, but it's how the list works.