Expand/Collapse HTML divs simultaneously - javascript

I am trying to create a row of HTML divs that expand and collapse when you click on them. Please see this jsFiddle to get an idea of what I mean: http://jsfiddle.net/Lm6Pg/3/. (You may have to expand the result pane or use the full screen result to get all the divs to appear on a single row.)
Currently, I am using One% CSS Grid to get all the divs to appear on one row, then toggling different CSS column classes to expand and collapse the divs according to the current state and what div was clicked.
<div id="content" class="onepcssgrid-1200">
<div class="onerow">
<div class="tile col4" id="about">
<h3>About</h3>
<div class="text">
<p>Lorem ipsum dolor sit amet.</p>
</div>
</div>
<div class="tile col4" id="other">
<h3>Other</h3>
<div class="text">
<p>Lorem ipsum dolor sit amet.</p>
</div>
</div>
<div class="tile col4 last" id="stuff">
<h3>Stuff</h3>
<div class="text">
<p>Lorem ipsum dolor sit amet.</p>
</div>
</div>
</div>
Here is my javascript (I've changed it from what's in the jsFiddle because I was messing around with dynamically determining which column classes needed to be toggled):
$(".tile").click(function() {
var tile = $(this);
var otherTiles = tile.siblings();
var currentSelectedTile = otherTiles.filter(".selected");
var unselectedOtherTiles = otherTiles.not(".selected");
var otherTileWeight, tileWeight;
if (currentSelectedTile.length) {
otherTileWeight = 3;
tileWeight = 3;
} else {
otherTileWeight = 4;
tileWeight = 4;
}
tile.toggleClass("selected col" + tileWeight + " col6", 600);
currentSelectedTile.toggleClass("selected col3 col4", 600);
unselectedOtherTiles.toggleClass("col" + otherTileWeight + " col3", 600);
});
This seems like a lot of code that might be wrapped up in a jQuery UI function or some other library that eluded me when I put this together. Is there an easier, more concise, or just plain better way to do this? The solution does not need to use jQuery nor One% CSS Grid, any library is fine. However, it does need to be in a responsive layout that preferably still functions as you'd expect when the divs are on top of each other.

Related

Trying to figure out how to close old content when switching tabs

I have a very basic tab setup on my website and I managed to show the content I need when I press a tab. However, that content stays visible when I click another tab.
I also tried to write a function to remove all the clicked-on tab content before showing me the next one but the problem is it gives me an addEventListener error and it's been driving me crazy for the last couple of days.
const tabs = function(panelClass, numClass, techNum) {
document.getElementById(panelClass).addEventListener("click", () => {
removeStuff();
document.querySelector('.slide').style.display = "flex"
document.querySelector(numClass).style.display = "flex"
document.querySelector(techNum).style.display = "block"
});
};
function removeStuff(parameter) {
document.querySelector(parameter).addEventListener("click", () => {
document.querySelector('.slide').style.display = "none"
document.querySelector('.content-slide').style.display = "none"
document.querySelector('.picture').style.display = "none"
});
}
removeStuff('tab1');
removeStuff('tab2');
tabs('tab1', '.two', ".p2");
tabs('tab2', '.one', ".p1");
<img class='picture p2' src="./picture.jpg" height=140px;alt="">
<img class='picture p1 ' src="./picture.jpg" height=140px;alt="">
<div class="slide">
<!-- Slide 1 -->
<div class="content-slide active one">
<h3>Some text</h3>
<p>Lorem ipsum dolor sit.</p>
</div>
<!-- Slide2 -->
<div class="content-slide two">
<h3>Some Text</h3>
<p>Lorem ipsum dolor </p>
</div>
</div>
The easiest and better approach would be to check that document.getElementById(someID) is not null before adding an event listener to it:
Demo code:
const tabs = function(panelClass, numClass, techNum) {
var panel = document.getElementById(panelClass);
if(panel){
panel.addEventListener("click", () => {
removeStuff();
document.querySelector('.slide').style.display = "flex"
document.querySelector(numClass).style.display = "flex"
document.querySelector(techNum).style.display = "block"
});
}
};
function removeStuff(parameter) {
var tab = document.querySelector(parameter);
if(tab){
tab.addEventListener("click", () => {
document.querySelector('.slide').style.display = "none"
document.querySelector('.content-slide').style.display = "none"
document.querySelector('.picture').style.display = "none"
});
}
}
removeStuff('tab1');
removeStuff('tab2');
tabs('tab1', '.two', ".p2");
tabs('tab2', '.one', ".p1");
<img class='picture p2' src="./picture.jpg" height=140px;alt="">
<img class='picture p1 ' src="./picture.jpg" height=140px;alt="">
<div class="slide">
<!-- Slide 1 -->
<div class="content-slide active one">
<h3>Some text</h3>
<p>Lorem ipsum dolor sit.</p>
</div>
</div>
<!-- Slide2 -->
<div class="content-slide two">
<h3>Some Text</h3>
<p>Lorem ipsum dolor </p>
</div>
</div>
If this an actual copy-and-paste of your code, a couple of things stand out:
You closed your main slide div before "content-slide two". Given the final <div> tag in your markup...that doesn't look like what you meant to do. One way or another, you have an extra div tag.
Your call to removeStuff() in the tabs function does not pass a parameter. That's going to cause some issues with querySelector; you'd be invoking addEventListener on an undefined object in the removeStuff() function.
It's likely that these two things are part of the problems you're experiencing.

How to shrink element by clicking on it or either of its siblings - vanilla js (may be useful for styling effects or accordion)

I have three panels, one by another in a row. When I click on one of them, its width increases. When I click on any of other two, the panel which previously increased width shrinks and the newly clicked panel widens. However, I would like to be able to shrink the just widened panel by clicking on it. I am struggling to find the solution but with no effect.
This is my code:
var panels = document.querySelectorAll('.panel'),
activePanel = document.querySelectorAll('.panel.open');
function toggleOpen() {
panels.forEach(function(item) {
if (item.classList.contains('open')) {
item.classList.remove('open');
item.classList.add('closed');
}
});
this.classList.remove('closed');
this.classList.add('open');
}
function closeActivePanel() {
if (activePanel.length > 0) {
activePanel.removeClass('open');
activePanel.addClass('closed');
}
}
panels.forEach(function(panel) {
panel.addEventListener('click', toggleOpen);
});
activePanel.forEach(function(aPanel) {
aPanel.addEventListener('click', closeActivePanel);
});
<div class="panel panel1">
<div class="panel__overlay"></div>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum</p>
<p>Lorem ipsum dolor sit amet</p>
</div>
<div class="panel panel2">
<div class="panel__overlay"></div>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum</p>
<p>Lorem ipsum dolor sit amet</p>
</div>
<div class="panel panel3">
<div class="panel__overlay"></div>
<p>Lorem ipsum dolor sit amet.</p>
<p>Lorem ipsume</p>
<p>Lorem ipsum dolor sit amet</p>
</div>
The function closeActivePanel just does not fire. And there is no error message.
The Time you query the DOM for the .panel.open elements they don't exist. You have to add the listener after you've added the class "open" to the element or implement a toggle logic in the click handler for the .panel elements.
Finally I found the solution for my problem. I think it would be very useful for accordion element for example. The goal was to have the possibility to increase width of one of elements by clicking on it (class 'open' produces this effect). And then to shrink it either by clicking on this element of any of other two beside it. Having the effect of shrinking the wide element by clicking on other two elements was easy. But to add functionality to shrink it by clicking on itself was quite a gymnastics. Event delegation turned out not to be helpful as I needed to know exact target of the event. Here is what I found working:
var panels = document.querySelectorAll('.panel');
function togglePanels(event) {
var target = event.target;
panels.forEach(function(item) {
if(item != target) {
item.classList.remove('open')
};
});
target.classList.toggle('open');
}
panels.forEach(function(panel) {
panel.addEventListener('click', togglePanels);
});

CKEditor get range to insert HTML at custom location

I am working on a module CKEditor Responsive Plugin for Drupal 7. I would need to insert a piece of HTML at a custom location above the cursor position. Below is the image which shows current cursor position:
The HTML of above part of the code looks like this:
<div class="ckeditor-col-container">
<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">
<div class="grid-12 twelvecol">
<p>lorem ipsum</p>
</div>
</div>
<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">
<div class="grid-12 twelvecol">
<p>lorem ipsum</p>
</div>
</div>
<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">
<div class="grid-12 twelvecol">
<p>lorem ipsum</p>
</div>
</div>
</div>
<p><br />
Sri Ramakrishna Vidya Kendra
</p>
<p></p>
The three div's which you see is the location which I now want to insert - that means I need to append my new set of div's below the last child div of the div with class ckeditor-col-container
I have gone through this SO link which talks about inserting HTML in given range: Insert HTML before an element in CKEditor
However, following are the challenges which I was not able to solve:
Traverse through the DOM above current cursor location to prepare the range with respect to the nearest div with class ckeditor-col-container
Get to the end of this (ckeditor-col-container) DOM and prepare range so that the new HTML elements would be inserted inside the scope of ckeditor-col-container
The DOM structure above cursor can be nested, but I am interested to find the nearest div with class ckeditor-col-container irrespective of the complicated nested DOM structures.
This is relatively easy to achive using jQuery object and traversing through DOM but CKEditor is cryptic with comparatively very less literature. Also point 3 in the above is tricky as a hierarchical datastructure has to be read in a flat fashion.
Any help would be appreciated.
Edit:
The example HTML piece of code which I want to insert is the same div's which you can find above:
lorem ipsum
And the final HTML will be like this:
<div class="ckeditor-col-container">
<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">
<div class="grid-12 twelvecol">
<p>lorem ipsum</p>
</div>
</div>
<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">
<div class="grid-12 twelvecol">
<p>lorem ipsum</p>
</div>
</div>
<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">
<div class="grid-12 twelvecol">
<p>lorem ipsum</p>
</div>
</div>
.
.
.
<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">
<div class="grid-12 twelvecol">
<p>lorem ipsum</p>
</div>
</div>
.
.
.
</div>
<p><br />
Sri Ramakrishna Vidya Kendra
</p>
<p></p>
The new 'inserted' div is the one which is displayed between the dots. I did not find a way to highlight the code when it is code-formatted.
The code doesn't do a complete traversing, but I think it can give you a pretty good place to start with.
The general idea is to take the current position of the cursor and start to check if any of the siblings (up and down the DOM tree) is the element we are looking for.
CKEDITOR.plugins.add( 'samplePlugin', {
icons: 'samplePluginIcon',
init: function( editor ) {
editor.addCommand( 'samplePlugin', {
exec: function( editor ) {
// First we need to find where our cursor is
var selection = editor.getSelection();
var range = selection.getRanges()[0];
// We go up and down the DOM tree, so we need the prev and next elements
var prevNode = range.getPreviousNode();
var nextNode = range.getNextNode();
// Save the container we are looking for
var container = null;
while (prevNode || nextNode) {
while (prevNode && prevNode.type == CKEDITOR.NODE_TEXT) {
prevNode = prevNode.getPreviousSourceNode();
}
if (prevNode && prevNode.hasClass('ckeditor-col-container')) {
container = prevNode;
break;
} else if (prevNode) {
prevNode = prevNode.getPreviousSourceNode();
}
while (nextNode && nextNode.type == CKEDITOR.NODE_TEXT) {
nextNode = nextNode.getNextSourceNode();
}
if (nextNode && nextNode.hasClass('ckeditor-col-container')) {
container = nextNode;
break;
} else if (nextNode) {
nextNode = nextNode.getNextSourceNode();
}
}
// In case we found the container we are looking for - just append some HTML to it.
if (container) {
container.appendHtml('<div class="hundred-hundred-fifty-fifty-thirtythree-thirtythree">'+
'<div class="grid-12 twelvecol">'+
'<p>lorem ipsum</p>'+
'</div>'+
'</div>')
}
}
});
editor.ui.addButton( 'samplePlugin', {
label: 'samplePlugin',
command: 'samplePlugin',
toolbar: 'insert'
});
}
});

How to get all elements but div (for example), including nested ones?

This is probably a long shot but is there a way to get a collection of elements in JQuery or not without a specific tag/class/id, even the nested ones?
Let's say I have this piece of code:
<div class="container1">
Lorem ipsum dolor sit amet
<br><br>
</div>
<p>This is p 1
<div class="footer">
tessssst
<p>
p test
</p>
</div>
</p>
<div class="container">
tessst
</div>
<p>This is p 2
<div id="someID" class="container">
tessssst 2
</div>
</p>
Now, I'm using JQuery like this, in order to find all but div tags, including descendants:
$('body').find(':not(div, div *)')
In the result collection, I still get the div inside the p elements and I don't want that.
Is there any way to achieve that? I know that this div is part of h1 and since I just want to select elements and not removing or doing DOM manipulations it could be a weird thing to wish for but this is what I need.
The bigger problem - I need to retrieve all text nodes but to exclude some tags/classes/IDs. I'm doing so as suggested here but it's not good enough.
Thanks.
The code you provided seems to work, but maybe you could try this?
$('body').find("*").filter(":not(div)");
There is a jQuery function not(), which removes matching elements from the current set.
So all you need to do is
var nonDivs = $('body').find('*').not('div');
You can do that by first selecting all elements, then selecting <div>s and their descendants, and filtering the "all" results with the "divs" result using .not() (the red style is used to mark the matched set):
var all = $('body').find('*');
var divAll = $('body').find('div, div *');
var nonDiv = all.not(divAll);
nonDiv.css('color', 'red');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container1">
Lorem ipsum dolor sit amet
<br><br>
</div>
<p>This is p 1
<div class="footer">
tessssst
<p>
p test
</p>
</div>
</p>
<div class="container">
tessst
</div>
<p>This is p 2
<div id="someID" class="container">
tessssst 2
</div>
</p>
In the filter function check if it's a text node and also check all of its parents for class, id, tag name - whatever selectors you would like to filter by.
var textnodes = $('body').find("*").contents()
.filter(function () {
return (this.nodeType === 3 && $(this).parents("div").length <= 0);
})
.each(function () {
console.log($(this).text());
});
Here's a working example: https://jsfiddle.net/hracw15o/3/

jQuery Traversal -- Find an Element Relative to Common Ancestor

I frequently find I'm needing to select an element that is nearby, typically within a common container, but which is not a sibling or within the same "tree line". For example, given this HTML:
<div id="container-left" class="container">
<div class="sidebar">
<button class="more-link">Show Extras</button>
</div>
<div class="content">
<div class="tidbits">
<p>Lorem ipsum beep bop boop</p>
<p class="extra hidden">Exxtra info about lorem ipsum!</p>
</div>
</div>
</div>
<div id="container-right" class="container">
<div class="sidebar">
<button class="more-link">Show Extras</button>
</div>
<div class="content">
<div class="tidbits">
<p>Lorem ipsum beep bop boop</p>
<p class="extra hidden">Exxtra info about lorem ipsum!</p>
</div>
</div>
</div>
What I'll do is attach an event listener to the "Show Extras" buttons, which target the p tags with class of extra, and on click, toggle the hidden class. So (using jQuery) I typically select like this:
$(".more-link").on("click",function(){
var $this = $(this);
var $extraElement = $this.closest(".container").find(".extra");
$extraElement.toggleClass("hidden");
});
My question: is there a better way to select the extra element than the .closest().find() combo? Something about it just feels a little clunky.
well.. you could navigate to sidebar siblings and find extra... but the mess would be similar...
Other way to do it is to generate a HTML5 data attribute at button:
<button class="more-link" data-extra='#some-generated-extra-id'>Show Extras</button>
...
<p id='some-generated-extra-id' class="extra hidden">Exxtra info about lorem ipsum!</p>
your code:
$(".more-link").on("click",function() {
var $extraElement = $($(this).data('extra'));
$extraElement.toggleClass("hidden");
});

Categories

Resources