I have a Fact box where the content can vary from a p, li or img tags, and the content length may also vary. Therefore, I have tried to make a function that only shows a part of the content, and a button to "read more".
But my problem is that when I use the substring to count where to start, and where to stop, and put the button "read more" in, but it's take both the HTML text and the plain text, but i only need the plain text.
So my question is: How do I remove all HTML text and just get plain text so it does not matter what kind of content that is in my facts box, it will still only show a portion of text, and hide the rest, with a button with option to show more. ??
My function that only show parts off the fact box:
$(document).ready(function () {
var showChar = 100;
var ellipsestext = "...";
var moretext = "Read more";
var lesstext = "less";
$('.showMoreLess').each(function () {
var content = $(this).html();
if (content.length > showChar) {
var c = content.substr(0, showChar);
var h = content.substr(showChar - 1, content.length - showChar);
var html = c + '<span class="moreellipses">' + ellipsestext + ' </span><span class="morecontent"><span>' + h + '</span> ' + moretext + '</span>';
$(this).html(html);
}
});
$(".morelink").click(function () {
if ($(this).hasClass("less")) {
$(this).removeClass("less");
$(this).html(moretext);
} else {
$(this).addClass("less");
$(this).html(lesstext);
}
$(this).parent().prev().toggle();
$(this).prev().toggle();
return false;
});
});
and my HTML, and agian it may vary what it contains
<div class="fact-banner">
<h4 class="title">fact headline</h4>
<div class="showMoreLess">
<div class="text">
<p>blablabla</p>
<p>blablabla</p>
<p>blablabla</p>
<p>blablabla</p>
</div
<div class="img">
<div class="image-banner text" style="padding-top: 0;margin-top:0">
<div class="image">
<img src="/bla.jpg" alt="bla" />
</div>
<div class="title">
<p>imageText</p>
</div>
</div>
</div>
<div class="text" style="padding-top: 0; padding-bottom: 0">
Relevant site to the article
</div>
</div>
I hope i make sense :)
Try using .text() instead of .html():
var content = $(this).text();
The wording of your question is definitely confusing. I think what you want is overflow:hidden; and not actually removing tags.
Append a .text-wrapper to your .text. .text-wrapper will have a fixed height when collapsed, and a height auto when expanded. .text-wrapper will have overflow: hidden.
Here's a quick codepen:
http://codepen.io/anon/pen/mVZNvv
Related
I am creating a news feed with VueJS and I have run into a bit of a problem with rendering the content. The API I am using sadly I am unable to change to suit my need properly at this time. The API gives me all the content already in HTML tags and it can also include images and lists and all the other basics. What I want to do is create a "read more" section which will render the first 20 words if just the text of the first "p" tag and stop there.
Does anyone know a quick and efficient way of doing this with JS?
My current display VueJS render is the following:
<div v-for="news_item in news_items">
<div v-bind:class="{ 'col-md-4': display}">
<div class="card">
<div class="header">
<h2>
{{news_item.title}} <small>{{news_item.subtitle}}</small>
</h2>
</div>
<div class="body" style="padding-top: 0">
<div class="row" style="margin-right: -20px; margin-left: -20px;">
<div class="col-md-12"
style="padding-left: 0px; padding-right: 0px;">
<img :src="news_item['thumbnail']"
class="img-responsive smaller-img" alt=""
style=" margin: 0 auto; max-height: 250px;">
</div>
</div>
<div class="row">
<div class="col-md-12">
<div v-html="news_item.content"></div>
</div>
</div>
</div>
</div>
</div>
</div>
This is the perfect time to use a directive:
https://v2.vuejs.org/v2/guide/custom-directive.html
See the codepen here: https://codepen.io/huntleth/pen/GOXaLo
Using the trim directive, you can change the content of the element. In the example above, it will show the first 5 words followed by an ellipsis.
If you're just after a pure js solution, this should do it:
var resultString = str.split(' ').slice(0, 20).join(" ");
You could use the trim directive and search the el for any p tags, and then change their content accordingly.
You don't appear to have tried anything yet, so I'll just give you these pointers. If you run into specific problems, ask again.
Make a component
The component should receive the html as a prop
The component should have a data item to control whether it is expanded
The component should have a computed that gets the first 20 words of the first paragraph tag. You can use textContent to get text from an HTML node.
The computed is the most likely part to pose a challenge. It will look something like this
blurb() {
const div = document.createElement('div');
div.innerHTML = this.content; // this.content is the prop
const firstP = div.querySelector('p');
const text = firstP.textContent;
const match = text.match(/(\S+\s*){0,20}/);
return match[0];
}
Rough implementation, Pure Js approach
document.getElementById("addContent").onclick = display;
document.getElementById("ellipsAnchor").onclick = hideEllipsis;
function display() {
document.getElementById("instruction").classList+= " hide";
let content = document.getElementById("inputbox").value;
if(content.length > 30) {
let sliced = content.slice(30);
let unsliced = content.substring(0,29);
let spantag = document.createElement("span");
spantag.className = "toReplace hide"
let text = document.createTextNode(sliced);
spantag.appendChild(text);
let spantag1 = document.createElement("span");
let text1 = document.createTextNode(unsliced);
spantag1.appendChild(text1);
let contentTag =document.getElementById("content");
contentTag.appendChild(spantag1)
contentTag.appendChild(spantag)
document.getElementById("ellipsis").classList -= "hide";
}
}
function hideEllipsis(){
document.getElementById("ellipsis").classList += " hide";
document.querySelectorAll("span.hide")[0].classList -= " hide"
}
.hide {
display : none;
}
<textarea type="text" id="inputbox"></textarea>
<button id="addContent">
Show content
</button>
<div id="content">
</div>
<div class="hide" id="ellipsis">
Read More..
</div>
<div id="instruction">
Type more than 30 characters and click show content
</div>
You can write a vue directive to solve this.
Set max-height to the div.
count the words and append "Read more.." link to the content.
Add a click event to 'read more' to expand the DIV to full height.
For example see this codepen
let handler = ""
Vue.directive("viewmore", {
inserted: function (el, binding){
let maxlines = binding.value
let lineheight = parseFloat(getComputedStyle(el).lineHeight)
let paddingtop = parseFloat(getComputedStyle(el).paddingTop)
let lines = (el.clientHeight) / lineheight ;
let maxheight = (lineheight * maxlines) + paddingtop + (lineheight/5)
if(lines>maxlines){
el.classList.add('vmore')
el.style.maxHeight = maxheight + 'px'
el.addEventListener('click', handler = ()=> {
el.style.maxHeight = ""
el.scrollIntoView({behavior: "smooth"})
el.removeEventListener('click', handler)
el.classList.remove('vmore')
})
}
},
unbind: function (el, binding) {
el.removeEventListener('click', handler)
handler = ""
}
});
https://codepen.io/dagalti/pen/vPOZaB .
it works based on the lines in the content.
Code : https://gist.github.com/dagalti/c8fc86cb791a51fe24e5dc647507c4a3
Expanding on the answers by tom_h and Roy J, here's what I'm using in my vue application to make the ellipsis clickable:
Vue.component("ellipsis", {
template: "#ellipsis-template",
props: ['content'],
data: function() {
return {
wordLength: 3, // default number of words to truncate
showAll: false
}
}
});
<script type="text/x-template" id="ellipsis-template">
<span v-if="content.split(' ').length>wordLength && showAll">{{content}}
(less)
</span>
<span v-else-if="content.split(' ').length>wordLength && !showAll">
{{content.split(" ").slice(0,wordLength).join(" ")}}
...
</span>
<span v-else>{{content}}</span>
</script>
To call it:
<ellipsis :content="someData"></ellipsis>
I have the following elements:
<div contenteditable="true" id="write">
<div class="tooltip">
<span>test</span> <!--text to be written in text area-->
<span class="tooltiptext"> <!--holds words to be displayed on hover-->
<span class="popUpWord">hello</span>
<br>
<span class="popUpWord">dog</span>
</span>
</div>
<div class="tooltip">
<span>test</span>
<span class="tooltiptext">
<span class="popUpWord">hello</span>
<br>
<span class="popUpWord">test</span>
</span>
</div>
</div>
These basically show a pop-up similar to the following - http://www.w3schools.com/css/tryit.asp?filename=trycss_tooltip
On hover event of elements having the '.tooltop' class (words to be displayed inside the pop-up area), I would like to swap the word which is hovered in the pop-up area, with the word displayed in the text area after a couple of seconds.
I did the following function:
//--choosing word from pop-up list--//
$(document).on("mouseover", ".popUpWord", function(e)
{
if(!timeoutId)
{
timeoutId=window.setTimeout(function()
{
timeoutId=null;
e.currentTarget.innerHTML = e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerText;
/*not working*/ e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerHTML = e.currentTarget.innerHTML; //Although the elements I want to swap are referred to correctly,the element's text is not changing. (Tried using innerText)
},1500);
}
}).on('mouseout', '.popUpWord', function(e)
{
if(timeoutId)
{
window.clearTimeout(timeoutId);
timeoutId=null;
}
});
However, in the line marked not working - the element's text is not changing. And it is being referred to correctly.
Any help is greatly appreciated.
Thanks
Its because you are assigning the value of tooltip to the text element and re-assigning it to the tooltip.
Try this:
timeoutId=null;
var text = e.currentTarget.innerHTML;
e.currentTarget.innerHTML = e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerText;
e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerHTML = text;
You are trying to locate the source and destination elements in a way that can only lead to brittleness and errors. Give elements you know you want to work with ids and reference them that way.
Try this out. Hover over "helo".
var orig = $("orig");
var hold = $("hold");
var timeoutId = null;
$(".popUpWord").on("mouseover", function(e){
if(!timeoutId) {
timeoutId = setTimeout(function() {
e.target.innerHTML = original.textContent;
original.innerHTML = e.target.innerHTML;
},1500);
}
}).on('mouseout', function(e){
if(timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contenteditable="true" id="write">
<div class="tooltip">
<span id="original">test</span> <!--text to be written in text area-->
<span class="tooltiptext"> <!--holds words to be displayed on hover-->
<span id="hold" class="popUpWord">hello</span>
<br>
<span class="popUpWord">dog</span>
</span>
</div>
<div class="tooltip">
<span>test</span>
<span class="tooltiptext">
<span class="popUpWord">hello</span>
<br>
<span class="popUpWord">test</span>
</span>
</div>
</div>
You will need to store the new text strings in a variable. Otherwise you are just setting it right back:
timeoutId=window.setTimeout(function()
{
var child = e.currentTarget,
parent = e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0],
childtext = child.innerText,
parenttext = parent.innerText
child.innerHTML = parenttext;
parent.innerHTML = childtext;
},1500);
I am trying to make a post edit using jquery. But i have a problem with image think.
I have created this DEMO from codepen.io .
In this demo you can see there are two edit button. If you click the edit1 then the image delete (x) button will be come on the right top bar but that will come just one time. It need to be come two delete button because there are two image. What is the problem on there and how can i fix that problems. Anyone can help me in this regard ?
JS
$(document).ready(function() {
$("body").on("click", ".editBtn", function(event) {
event.target.disabled = true;
var ID = $(this).attr("id");
var selected = $("#messageB" + ID + " .postInfo img").parent().html();
var currentMessage = $("#messageB" + ID + " .ptx").html();
var editMarkUp = '<div class="edi"><div class="del">x</div>' + selected + '</div><div class="edBtnS"><div class="edSv">Save</div><div class="cNeD" id="' + ID + '">Cancel</div></div><textarea rows="5" cols="80" id="txtmessage_' + ID + '">' + currentMessage + '</textarea>';
$("#messageB" + ID + " .postInfo").html(editMarkUp);
var data = $('#txtmessage_' + ID).val();
$('#txtmessage_' + ID).focus();
$('#txtmessage_' + ID).val(data + ' ');
});
$("body").on("click", ".cNeD", function(event) {
$(".editBtn").prop('disabled', false);
var ID = $(this).attr("id");
var currentMessageText = $("#txtmessage_" + ID).html();
$("#messageB" + ID + " .ptx").html(currentMessageText);
});
});
HTML
<div class="container">
<div class="postAr" id="messageB1">
<div class="postInfo">
<img src="http://hdwallpaperia.com/wp-content/uploads/2013/10/Home-Sweet-Home-Wallpaper.jpg" id="1">
<img src="http://www.dam7.com/Images/Puppy/images/myspace-puppy-images-0005.jpg" id="1">
</div>
<div class="ptx"> fdasfads fasd fadsf adsf adsf adsf asd fasd f dfsas</div>
<div class="editBtn" name="edit" id="1">Edit1</div>
</div>
</div>
<div class="container">
<div class="postAr" id="messageB2">
<div class="postInfo">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701">
fdasfads fasd fadsf aldsf adsf adsf asd fasd f dfsassssg
</div>
<div class="editBtn" name="edit" id="2">Edit2</div>
</div>
</div>
I found that there are 2 main issues there.
you use html() function which get the HTML contents of the first element in the set of matched elements. take a look document
-> you will get html instead of src of img. But later when you try to set src attribute during creating an 'edit area' so that is the mainly reason why image doesn't
display (.attr() function is better to get src attribute)
same wrong logic with message ( .text() function could be the better
solution in this case)
don't forget to check if you have create an edit area or not. At the moment every time it will create a new "edit area". Duplicate !
Hope it will help you a bit.
I have an issue with showing and hiding content. I am not able to hide or show full content.
My problem is this class for styling faq-link-style. When I remove it, my read more and read less buttons/links work. I think it might be the way I wrote it in JQuery. I tried many different versions but could not get it working.
Example: $('.faq-link-style .read-more')
Is there another way to improve my code? I don't want to duplicate my code. Example: I don't want to get the content twice. Once for an intro (read less, faq-intro) And the other time for the full text (read more, faq-info)
Thanks in Advance
<div class="col-lg-4">
<div class="faq-all">
<div class="faq-item">
<h2><?php the_title(); ?></h2>
<article>
<div class="faq-intro">
<?php the_content(); ?>
</div>
<div class="faq-info">
<?php the_content(); ?>
</div>
<div class="faq-link-style">
Read More
Read Less
</div>
</article>
</div>
</div>
</div>
JQuery:
(function ($) {
$(document).ready(function () {
var showChar = 600; // How many characters are shown by default
$('.faq-intro').each(function() {
var content = $(this).html();
if(content.length > showChar) {
var s = $(this).html();
var c = content.substr(0, showChar);
var h = content.substr(showChar, content.length - showChar);
var html = c + " ... ";
$(this).html(html);
}
if(content.length < showChar){
$(this).parent().find('.read-less').hide();
$(this).parent().find('.read-more').hide();
}
});
$('.faq-info').hide();
$('.read-less').hide();
//read more
$('.read-more').click(function(){
$(this).siblings('.faq-info').show();
$(this).hide();
$(this).parent().find('.read-less').show();
$(this).siblings('.faq-intro').hide();
})
$('.read-less').click(function(){
$(this).siblings('.faq-info').hide();
$(this).hide();
$(this).parent().find('.read-more').show();
$(this).siblings('.faq-intro').show();
});
});
})(jQuery);
Your selectors are wrong, it should be:
$('.read-more').click(function(){
$(this).hide()
.siblings('.read-less').show().end()
.parent()
.siblings('.faq-intro').hide().end()
.siblings('.faq-info').show();
});
$('.read-less').click(function(){
$(this).hide()
.siblings('.read-more').show().end()
.parent()
.siblings('.faq-intro').show().end()
.siblings('.faq-info').hide();
});
jQuery(".cms-outdoor-furniture-covers .col-main a").each(function (i, html) {
return html.replace(" >", "<span class=\"no-underline\">$&</span>");
});
I am trying to figure out how to replace all " >" in hyperlinks with "<span class="no-underline"> ></span>"
markup:
<div class="cms-outdoor-furniture-covers">
<div class="col-main">
random text ending with >
</div>
</div>
Goal I want to achieve is- keep underline for random string, but wrap ending- " >" in span to remove underline from there.
<div class="cms-outdoor-furniture-covers">
<div class="col-main">
random text ending with<span class="no-underline"> ></span>
</div>
</div>
Do you mean to do:
jQuery(".cms-outdoor-furniture-covers .col-main a").each(function (i, val) {
var html = jQuery(this).html();
if( html.search(' >') > -1 ) {
jQuery(this).html( "<span class='no-underline'>" + html.replace( " >", "" ) + "</span>" + " >" );
}
});
and CSS:
a { text-decoration: none }
.no-underline {text-decoration:underline;}
You need to remove the underline from the link altogether (narrow down the CSS to only affect those links if you want) and then add a class to the text you want underlined instead.
Here's the demo: http://jsfiddle.net/qz5R8/2/
Just wrap the a-tags:
$('.cms-outdoor-furniture-covers .col-main a')
.wrap("<span class='no-underline'></span>");