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");
});
Related
I have several boxes and inside each of the boxes I have different title and headline elements.
I would like to know how I can get the text of the desired element inside the corresponding box that has been clicked, not all of the text elements.
Here's what I'm working with:
box.onclick = function(){
alert($(this).text());
};
This displays all of the text elements data in the alert box. I would like to solely target a single specified text element that has an id to get the text from it instead.
Any help would be appreciated.
You can use find() to target the child element:
$('.box').on('click', function() {
console.log($(this).find('h3').text())
})
.box{
background: #adadad;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='boxes'>
<div class='box'>
<h3>title 1</h3>
<h4>subtitle 1</h4>
</div>
<div class='box'>
<h3>title 2</h3>
<h4>subtitle 2</h4>
</div>
<div class='box'>
<h3>title 3</h3>
<h4>subtitle 3</h4>
</div>
</div>
HTML
<div id = "content">
<div id = "box">
<p>This is the box content 1 </p>
</div>
<div id = "box">
<p>This is the box content 2</p>
</div>
</div>
JS
$('*[id*=box]:visible').each(function(index) {
$(this).click(function() {
alert($(this).text());
});
});
Test it here:
https://codepen.io/bmvr/pen/WMZPXW
I want to get the text on click with a certain class but nothing works. I have a div with 2 p tags and I want to get both separate. Also, I want to append them. .append() appends but just keeps adding all targeted events. empty().append() gives me random results (on first click it works, on second I get half of the text etc). Ive tried most of what I could find on stack overflow but nothing helped. Any help would be great!
Ive tried:
$(event.target).text(); //that gives me the text, but not both p elements separate
$(this).hasClass('.video-title'); //only returns to me true/false
var title= document.getElementsByClassName("video-title")[0].innerHTML; //doesn't give me the current element.
$('p.video-title').innerHTML; //doesnt help either
HTML
<div class="video-container">
<iframe id="vid_frame" src="https://www.youtube.com/embed/xxx?rel=0&showinfo=0&autohide=1" width="900" height="450"></iframe>
<div id="video-info"></div>
</div>
<div class="col-sm-4 video-col">
<div class="video-wrapper" onClick="attachSrc('yyy', event)">
<img src="assets/images/thumbnail/yourHeart_official.jpg" width="260" height="160">
<div class="overlay">
<p class="video-title">title 1</p>
<p class="video-author">author 1</p>
</div>
</div>
</div>
<div class="col-sm-4 video-col">
<div class="video-wrapper" onClick="attachSrc('xxx', event)">
<img src="assets/images/thumbnail/yourHeart_karaoke.jpg" width="260" height="160">
<div class="overlay">
<p class="video-title">title 2</p>
<p class="video-author">author 2</p>
</div>
</div>
</div>
<script>
function attachSrc(id, event) {
var text = $(event.target).text();
$('#video-info').append(text);
}
</script>
As you are using jQuery, I would recommend you to use unobtrusive event handler and use .on() to attach event handlers.
Here in example I have attached event with wrapper element and DOM traversal method to traverse and target desired element.
And to persists arbitrary data use data-* prefixed custom attribute which can be fetched using .data(key)
<div class="video-wrapper" data-id="yyy">
Script
$('.video-col').on('click', '.video-wrapper', function() {
var elem = $('#video-info').empty();
var title = $(this).find('.video-title').text();
elem.append(title);
console.log(title);
var author= $(this).find('.video-author').text();
elem.append(author);
console.log(author);
console.log($(this).data('id'));// To fetch custom data associated with element
});
$(function() {
$('.video-col').on('click', '.video-wrapper', function() {
console.clear();
var elem = $('#video-info').empty();
var title = $(this).find('.video-title').text();
elem.append(title);
console.log(title);
var author = $(this).find('.video-author').text();
elem.append(author);
console.log(author);
console.log($(this).data('id')); // To fetch custom data associated with element
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="video-container">
<div id="video-info"></div>
</div>
<div class="col-sm-4 video-col">
<div class="video-wrapper" data-id="yyy">
<img src="assets/images/thumbnail/yourHeart_official.jpg" width="260" height="160">
<div class="overlay">
<p class="video-title">title 1</p>
<p class="video-author">author 1</p>
</div>
</div>
</div>
<div class="col-sm-4 video-col">
<div class="video-wrapper" data-id="xxx">
<img src="assets/images/thumbnail/yourHeart_karaoke.jpg" width="260" height="160">
<div class="overlay">
<p class="video-title">title 2</p>
<p class="video-author">author 2</p>
</div>
</div>
</div>
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'
});
}
});
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/
With the example below, I'd like to select the contents of the < h3 > tags for each block, and move them inside the 'title' divs (without the h3 tags). Is this possible?
<div id="block1">
<div class="title">
</div>
<div class="content">
<h3>Title 1</h3>
</div>
</div>
<div id="block2">
<div class="title">
</div>
<div class="content">
<h3>Title 2</h3>
</div>
</div>
Online demo: http://jsbin.com/ezuxo
// Cycle through each <div class="content"></div>
$(".content").each(function(){
// Find its first h3 tag, and remove it
var heading = $("h3", this).remove();
// Set the text of the h3 tag to the value of the previous div (.title)
$(this).prev().html($(heading).html());
});
First off, I'd assign a class to the "blocks", let's call it "block" for now. Then do this:
$(".block").each(function() {
$(".title", this).html($(".content h3", this).html());
}