Add class on highlighted elements? - javascript

Is it possible to add a class on highlighted elements? For example: If I Select the .block-2, .block-3, .block-4, and press Enter key then all of these blocks should add a new class. I mean I need to get all of these elements on highlight selection when pressed the Enter key.
<div class="root">
<div class="block-1"> 1st </div>
<div class="block-2"> 2nd </div>
<div class="block-3"> 3rd </div>
<div class="block-4"> 4th </div>
<div class="block-5"> 5th </div>
</div>

Youll want to use the selections api to do this. You can use containsNode to do the rest, if your are ie9+.
https://developer.mozilla.org/en-US/docs/Web/API/Selection
Assuming you can use containsNode you should just be able to listen to keydown, filter by Enter, and then finaly query the document for the selection and apply classes depending on if the block nodes are contained within the selection.
const $root = document.querySelector('.root');
document.addEventListener('keydown', (e) => {
if (e.code == "Enter") {
const selection = document.getSelection();
console.log(selection)
for (const $child of $root.children) {
if (selection.containsNode($child) || $child.contains(selection.anchorNode) || $child.contains(selection.focusNode)) {
$child.classList.add('selected');
} else {
$child.classList.remove('selected');
}
}
}
});
This code was thrown together in about 3 minutes, is sub optimal, and should be reviewed before being used. You can see it in action # https://jsfiddle.net/z0gh7eck/2/.

According to jandy's answer to stackovweflow. There is no event that "text was selected". But you can bind mouseup event to check if there was a selection then add a class.

Related

Open hidden elements one at a time?

please I am trying to create a FAQ like functionality, I have some elements hidden so when I click on a button it opens and hides it. I have been able to do this but I am not getting what I actually want. I might have done something wrong I suppose. So, there are 5 elements with the same className, this will help me target them all and run a for loop to kind of break them apart. However if I click on this button to open one of the element the other ones open.
const openBtn = document.querySelectorAll(".openBtn")
const openContent = document.querySelectorAll(".openContent")
for(btn of openBtn) {
btn.addEventListener('click', () => {
for(content of openContent) {
if (content.classList.contains('hidden')) {
content.classList.remove('hidden');
content.classList.add('flex')
} else {
content.classList.remove('flex');
content.classList.add('hidden')
}
}
})
}
So as you can see, If I click on the chevron icon for just one of the wither About Us, Careers or just any of the 5 every other one opens. How do I fix this ?
Since you aren't going to post even the most general version of your HTML, here is a general outline.
First, each button gets a data attribute for target,then each FAQ div gets an ID attribute that matches the data target attribute.
I attach the click handler to the document and look for openBTN on the clicked element. Then I loop through every OPENED div to close it. Then I get the target data attribute and add the appropriate classes.
document.addEventListener("click", function(e) {
if (e.target.classList.toString().includes("openBtn")) {
let opened = document.querySelectorAll(".openContent.flex");
opened.forEach(function(el) {
el.classList.add("hidden");
el.classList.remove("flex");
});
let target = document.querySelector(e.target.dataset.target)
target.classList.remove("hidden");
target.classList.add("flex");
}
});
.hidden {
display: none
}
<button data-target="#faq1" class="openBtn">OPEN</button>
<div id="faq1" class="openContent hidden">1</div>
<button data-target="#faq2" class="openBtn">OPEN</button>
<div id="faq2" class="openContent hidden">2</div>
<button data-target="#faq3" class="openBtn">OPEN</button>
<div id="faq3" class="openContent hidden">3</div>
<button data-target="#faq4" class="openBtn">OPEN</button>
<div id="faq4" class="openContent hidden">4</div>

javascript/jquery: focus() not working

I want to focus a special id (in roundcube) by a keyboard shortcut. The html is
...
<div id="mainscreen">
<div id="messagetoolbar" class="toolbar">
<div id="mailview-left" style="width: 220px;">
<div id="mailview-right" style="left: 232px;">
...
I tried the following:
// Strg + Tab, um in Nachrichtenbereich zu kommen
...
else if (event.keyCode == 9 && event.ctrlKey) {
alert("taste erkannt");
//document.getElementById("messagetoolbar").focus();
//$("#messagetoolbar").focus();
setTimeout(function() { $('#messagetoolbar').focus(); alert("zeit"); }, 3000);
}
...
The first alert and the second alert is shown but no focus on id messagetoolbar. Does anybody have an idea?
Thank you very much.
Edit: I think I should describe it better: I want to mark the first line/email in the email-inbox in roundcube. The inbox is a table with a tr-tag...when I try your solution the first line is dotted, too, but with enter I can't open the mail and with other keys I can't MARK the first line/mail... I think I have to "simulate a left-klick" to get the first line marked...?
Now I tried to use jquery's .trigger. The html of the inbox-Table is
<table id="messagelist" class="records-table messagelist sortheader fixedheader">
<thead>
<tbody>
<tr id="rcmrow27428" class="message">
<td class="threads"></td>
<td class="date">16.04.2014 13:41</td>
<td class="fromto">
...
I tried to use...
$('#messagelist tr').eq(1).addClass('message selected focused').removeClass('unfocused').trigger("click");
...but it doesn't work: It adds an removes the classes but doesn't really focus the line :-( With "buttons" it works.
EDIT AGAIN: I think the file list.js of roundcube is important for that question. There I found the following:
/**
* Set focus to the list
*/
focus: function(e)
{
var n, id;
this.focused = true;
for (n in this.selection) {
id = this.selection[n];
if (this.rows[id] && this.rows[id].obj) {
$(this.rows[id].obj).addClass('selected').removeClass('unfocused');
}
}
// Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620)
// It looks that window.focus() does the job for all browsers, but not Firefox (#1489058)
$('iframe,:focus:not(body)').blur();
window.focus();
if (e || (e = window.event))
rcube_event.cancel(e);
},
Does anybody know how to modify or use referring to my question? Thank you!
Add tabindex=0 attribute to the div you want to foucs and you will be able to set focus on the div using .focus()
Do you want to highlight the div then you can use the jquery and following code to give highlight effect.
$("div").click(function () {
$(this).effect("highlight", {}, 3000);
});
You can not focus controls like div, span etc. You can move to that div if required using book marks.
Good morning,
I've read a few times now, that one can't focus table rows but only can focus elements, which accepts input by the user. Otherwise I think there must be a way to simulate a click on a table row by jquery/javascript! I tried the following:
document.onkeydown = function(event) {
...
else if (event.keyCode == 9 && event.ctrlKey) {
$('#messagelist tr').on('click', function () {
alert('I was clicked');
});
$('#messagelist tr').eq(1).click();
}
...
}
The alert is shown! But the row isn't "marked"!?

Detect click on "empty" area

Let's say we have next html/css: http://jsfiddle.net/rbDKm/2/
<div id="header">header</div>
<div id="content">
<div class="actions">
<div class="actions-row">
<div class="action ">home</div>
<div class="action ">search</div>
<div class="action ">settings</div>
</div>
<div class="actions-row">
<div class="action">...</div>
<div class="action">...</div>
<div class="action">...</div>
</div>
</div>
</div>
<div id="footer">footer</div>
What I need is to detect any clicks, which were made on an "empty" area.
In provided fiddle, empty area would be everything, which has white or orange color.
Is there any technique/approach for doing this?
With your markup as you have it now, I think that means you need to detect clicks outside of any div.action, #header or #footer.
To do that, you hook click on body, and then look at the path it took to get to body:
document.body.addEventListener("click", function(e) {
var elm = e.target;
while (elm !== document.body) {
if (elm.id === "header" || elm.id === "footer") {
return;
}
if (elm.tagName.toUpperCase() === "DIV" && /(?:^| )action(?:$| )/.test(elm.className)) {
return;
}
elm = elm.parentNode;
}
// If we got here, it's on one of the desired areas
});
There are a couple of variations on that you could do. For instance, instead of className.match you could use classList on modern browsers. And modern browsers also offer Element#closest which you could use instead of the loop.
Alternately, you hook click on body and hook click on the relevant other elements and prevent the click from propagating:
function stopTheEvent(e) {
e.stopPropagation();
}
document.body.addEventListener("click", function(e) {
// It's one of the desired areas
});
var list = document.querySelectorAll("#header, #footer, div.action");
var index;
for (index = 0; index < list.length; ++index) {
list[index].addEventListener("click", stopTheEvent);
}
...but that seems messier.
Note: In the above, I haven't allowed for IE8's lack of addEventListener. If you need to support IE8, look at attachEvent("onclick", ...) and use window.event.returnValue = false; to stop propagation (or use a library that abstracts these things away).
Use the "target" of the event, and check it's class/id
http://jsfiddle.net/yryQ6/
When you bind the click event, use the event, get the target and ask for it's class:
$('#content').on('click',function(e){
if ($(e.target).hasClass('action')) {
// click on an action
} else {
// click on smething that's not an action, in this case, action-row o actions, your empty area
}
}
EDIT: assuming you use jquery, you should be able to use something similar with javascript events

jQuery matching a single class against predefined list where element has multiple classes

I am trying to figure out if there is a way around doing .hasClass() multiple times to see if the active element I am working with has one of currently four specific classes, I am also trying to figure out the most optimized way to do this while the element(s) that are acting as the trigger (or the active element) has multiple classes in it mostly for styling purposes.
Example of the HTML:
<div class="form_row">
<div class="primary_row">1</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub">1a</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub subexists">1a</div>
<div class="secondary_row">2</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub subexists">1a</div>
<div class="secondary_row subexists">2</div>
<div class="secondary_row_sub">2a</div>
</div>
I am in the progress of currently building it up, so this is still a rough draft, but its safe to assume more classes will exist on various elements per the need. The Four main classes I am worried about are primary_row, primary_row_sub, secondary_row, secondary_row_sub. I am building a click handler like:
$('.form_row > div').click(function()
{
//code
});
in this click handler I want to be able to detect if the element clicked is one of the four mentioned above. Where if it is, I want to do something based on which. So determining which class is of the element clicked, rather than building four click handlers one for each type. I am hoping I can keep it optimized and contained to a single handler. Any ideas?
One option:
var classMap = {"one": function () { alert("one");},
"two": function () { alert("two");},
"three": function () { alert("three");}
}
, classes = "";
$('div').click(function (e) {
classes = this.className.split(" ");
for (key in classMap) {
if ($.inArray(key, classes) !== -1) {
classMap[key]();
}
}
});
http://jsfiddle.net/wp9X7/5/
if ($(this).is(".primary_row")) {
...
} elseif ($(this).is(".primary_row_sub")) {
...
} and so on

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