echo $nav gives code like this:
<ul>
<li class="someclass">sometext
<ul>
<li class="someclass">sometext</li>
<li class="spacer"></li>
<li class="someclass">sometext</li>
<li class="spacer"></li>
<li class="someclass">sometext</li>
<li class="spacer"></li>
<li class="someclass">sometext</li>
<li class="spacer"></li>
</ul>
</li>
<li class="spacer"></li>
<li class="someclass">sometext</li>
<li class="spacer"></li>
</ul>
There are list items with class spacer inside each child ul, after each normal list item.
How do I remove the spacer list items which are grandchildren of the main list, using PHP?
Example: <ul> <li> <ul> <li class="spacer">
I'm searching for a regular expression, which should erase <li class="spacer"></li> only in a child <ul> element.
If you don't have access to the $nav variable to remove it (which you likely do) then I'd just use CSS to hide it, something like this should work:
li ul li.spacer {
display:none;
}
If however you have access to $nav - delete that spacer li from the code. Simples.
Also, on a side note. having empty elements like that on the page as "spacers" is semantically bad. This should be handled via CSS, add margins/padding on other elements on the page, don't use a class of spacer, if you do then you may as well go back to using stray <br /> tags everywhere to create spaces.
$xml = new SimpleXMLElement($nav);
$spacers = $xml->xpath('li//li[#class="spacer"]');
foreach($spacers as $i => $n) {
unset($spacers[$i][0]);
}
echo $xml->asXML();
This is converting to XML (use a recent PHP 5.3 version and DOMDocument to export to HTML). Output:
<?xml version="1.0"?>
<ul>
<li class="someclass">sometext
<ul>
<li class="someclass">sometext</li>
<li class="someclass">sometext</li>
<li class="someclass">sometext</li>
<li class="someclass">sometext</li>
</ul>
</li>
<li class="spacer"/>
<li class="someclass">sometext</li>
<li class="spacer"/>
</ul>
How about str_replace?
$nav = str_replace('<li class="spacer"></li>','',$nav);
edited code below
Based on the new requirement this code works. I know its hacky and sloppy but it works:
$temp = explode("\n",$nav);
for ($i=0;$i<count($temp);$i++) {
if (strstr($temp[$i],"<ul>")) {
$nested_ul = 1;
}
if (strstr($temp[$i],"</ul>")) {
$nested_ul = 0;
}
if ($nested_ul==0) {
if (!strstr($temp[$i],"spacer")) {
$new_nav .= $temp[$i]."\n";
}
} else {
$new_nav .= $temp[$i]."\n";
}
}
echo $new_nav;
"Easily" is relative. It depends on a few things. If you want, modify where the $nav is getting generated from.
use preg_replace to replace the li tags:
$new_nav = preg_replace('/<li class="spacer"></li>/', '', $nav);
echo $nav;
There are multiple ways:
Do not create it. It will be easier if you do not create something you do not want. It will be easier to maintain. So if you have any control over what is generated into $var string, just change it.
Simply replace it like that: str_replace('<li class="spacer"></li>', $var).
Use some HTML parser and remove the nodes.
Use JavaScript to remove <li class="spacer"></li> on client side.
Use substr_replace and strpos instead of str_replace, and specify an offset just after the first spacer.
http://www.php.net/manual/en/function.substr-replace.php
http://www.php.net/manual/en/function.strpos.php
Add the following CSS
ul ul li.spacer { display: none; }
Try this:
$nav = str_replace('<li class="spacer"></li>', '', $nav);
Related
In a typical HTML as
<ol>
<li>
<span>parent</span>
<ul>
<li><span>nested 1</span></li>
<li><span>nested 2</span></li>
</ul>
</li>
</ol>
I try to get the contents of <li> elements but I need to get the parent and those nested under ul separately.
If go as
$ols = $doc->getElementsByTagName('ol');
foreach($ols as $ol){
$lis = $ol->getElementsByTagName('li');
// here I need li immediately under <ol>
}
$lis is all li elements including both parent and nested ones.
How can I get li elements one level under ol by ignoring deeper levels?
There are two approaches to this, the first is how you are working with getElementsByTagName(), the idea would be just to pick out the first <li> tag and assume that it is the correct one...
$ols = $doc->getElementsByTagName('ol');
foreach($ols as $ol){
$lis = $ol->getElementsByTagName('li')[0];
echo $doc->saveHTML($lis).PHP_EOL;
}
This echoes...
<li>
<span>parent</span>
<ul>
<li><span>nested 1</span></li>
<li><span>nested 2</span></li>
</ul>
</li>
which should work - BUT is not exact enough at times.
The other method would be to use XPath, where you can specify the levels of the document tags you want to retrieve. This uses //ol/li, which is any <ol> tag with an immediate descendant <li> tag.
$xp = new DOMXPath($doc);
$lis = $xp->query("//ol/li");
foreach ( $lis as $li ) {
echo $doc->saveHTML($li);
}
this also gives...
<li>
<span>parent</span>
<ul>
<li><span>nested 1</span></li>
<li><span>nested 2</span></li>
</ul>
</li>
I have this:
<ul class="nav navbar-nav">
<li>Link1</li>
<li>Link2 </li>
<li>Link3</li>
<li>Link4</li>
</ul>
I want to add active class so user knows on what page he is at.
My best idea is to have li items with unique id and in jquery check if id=1 to set "Link1" with active class.
Even tho this works, I can't stop wondering if there is a better solution, more elegant one?
If you have php there,as in your tag, you can compare $_GET values. Something like that:
<li <?=$_GET['order_by']=='new' ? 'class="active"' : '';?>>
<a>link</a>
</li>
I would do it server side like cssBlaster21895 has shown, but you can do it client-side with javascript, too.
var links = document.getElementsByClassName('nav')[0].getElementsByTagName('a'),
qString = window.location.search;
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (qString == link.getAttribute('href')) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
}
<ul class="nav navbar-nav">
<li>Link1</li>
<li>Link2 </li>
<li>Link3</li>
<li>Link4</li>
</ul>
This is my HTML part of code:
<ul>
<li> something,,,,... </li>
<li> something,,,,... </li>
<li> something,,,,... </li>
<li> something,,,,... </li>
<li>
<h5>Price</h5>
<span>100$</span>
</li>
</ul>
In my php I am using php-simple-dom for finding tags. So php part looks something like this:
foreach($html->find("li") as $li)
{
if(strpos($li->plaintext,"<h5>Price</h5>") !== false)
{
var_dump($li->plaintext); // result: string("<h5>Price</h5><span>100$</span>")
}
}
I have some other idea:
foreach($html->find("h5") as $h5)
{
if(strpos($h5->plaintext,"Price") !== false)
{
// finding some way to read next tag
}
}
What I need ?
I need to get <span> value. This is example, in real code there are more tags and multiple spans in one <li>. But point is that next tag contain wanted information.
I'm not pretty sure how many tags could be in one <li>, but I belive <span> you are looking for is always after <h5>. You can use method $e->next_sibling() as follows:
foreach ($html->find('li h5') as $h5) {
$price = $h5->next_sibling();
echo $price->plaintext;
}
So you want to get a value of a specific tag, you could find DOMDocument::getElementsByTagName useful.
Return Values
A new DOMNodeList object containing all the matched elements.
Here is how you would use it:
$html = <<< HTML
<ul>
<li> something,,,,... </li>
<li> something,,,,... </li>
<li> something,,,,... </li>
<li> something,,,,... </li>
<li>
<h5>Price</h5>
<span>100$</span>
</li>
</ul>
HTML;
$dom = new DOMDocument;
$dom->loadXML($html);
$prices = $dom->getElementsByTagName('span');
foreach ($prices as $price) {
echo $price->nodeValue, PHP_EOL;
}
The above example will output: 100$
Go ahead and try it with several prices. It works as excepted.
You might also find the DOM documentation useful.
Hi I am looking for a solution to add a class to every list item <li> which has a child item with a class of <span class="separator"> and a different class to <li> with an anchor link.
I use Joomla and the menu is being generated somewhat like this:
<ul class="menu">
<li class="item1"><span>Home</span></li>
<li class="parent item59"><span class="separator"><span>Demo</span></span></li>
<li class="item62"><span>Article</span></li>
<li id="current" class="parent active item27"><span>CMS</span>
<ul>
<li class="item50"><span>The News</span></li>
<li class="item48"><span>Web Links</span></li>
<li class="item65"><span class="separator"><span /></span></li>
<li class="item49"><span>News Feeds</span></li>
<li class="item66"><span class="separator"><span /></span></li>
<li class="item67"><span class="separator"><span /></span></li>
<li class="item68"><span class="separator"><span /></span></li>
</ul>
</li>
<li class="item71"><span class="separator"><span>Help</span></span></li>
</ul>
What I want is to add class "anclink" or "seplink" to the <li> depending on their child item so that the final output looks like below.
<ul class="menu">
<li class="item1 anclink"><span>Home</span></li>
<li class="parent item59 seplink"><span class="separator"><span>Demo</span></span></li>
<li class="item62 anclink"><span>Article</span></li>
<li id="current" class="parent active item27" anclink><span>CMS</span>
<ul>
<li class="item50 anclink"><span>The News</span></li>
<li class="item48 anclink"><span>Web Links</span></li>
<li class="item65 seplink"><span class="separator"><span /></span></li>
<li class="item49 anclink"><span>News Feeds</span></li>
<li class="item66 seplink"><span class="separator"><span /></span></li>
<li class="item67 seplink"><span class="separator"><span /></span></li>
<li class="item68 seplink"><span class="separator"><span /></span></li>
</ul>
</li>
<li class="item71 seplink"><span class="separator"><span>Help</span></span></li>
</ul>
How can I achieve this using PHP or even a jQuery solution will be fine.
Kindly help.
With jQuery:
$('#menu > li:has( > span.separator )').addClass('seplink');
$('#menu > li:has( > a )').addClass('anclink');
or:
$('#menu > li > span.separator').parent().addClass('seplink');
$('#menu > li > a').parent().addClass('anclink');
EDIT: I was just walking out the door when I left this solution, so I didn't have the chance to note that I'd strongly favor the second solution since it utilizes a valid CSS selector.
It will perform better in browsers that support querySelectorAll.
Without jQuery:
var ul = document.getElementsByClassName("menu");
var li = ul[0].getElementsByTagName("li");
for ( var i = 0; i < li.length; i++ ){
var class = li[i].className;
class += ( li[i].childNodes[0].tagName.toUpperCase() == 'A' ) ? ' anclink' : ' seplink';
li[i].className = class;
}
Edit
So you can eitheir use getElementsByClassName as defined in http://robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/ or use another method such as getElementsByTagName (in your case the root of your nested list is the first UL.
For testing purpose I'd advise you to paste this code in the HEAD of your HTML document. Also, you need to call this function once the window is loaded as you want to apply changes on the window's loaded elements.
<script type="text/javascript">
window.onload = function(){
var ul = document.getElementsByTagName("ul")[0];
var li = ul.getElementsByTagName("li");
for ( var i = 0; i < li.length; i++ ){
var myClass = li[i].className;
myClass += ( li[i].childNodes[0].tagName.toUpperCase() == 'A' ) ? ' anclink' : ' seplink';
li[i].className = myClass;
}
};
</script>
Adding a second answer, because I'd advise against loading the entire jQuery library if this is its only purpose.
An alternative would be to use Sizzle, which is the CSS selector engine that is included in jQuery. It is a much smaller download, and as such, may be a decent compromise.
You'd get the library, and use it like this:
var li_span = Sizzle('#menu > li > span.separator'),
li_a = Sizzle('#menu > li > a'),
s = li_span.length,
a = li_a.length;
while( s-- ) {
if( li_span[ s ].className === 'separator' ) {
li_span[ s ].parentNode.className += ' seplink';
}
}
while( a-- ) {
li_a[ a ].parentNode.className += ' anclink';
}
The downloaded file is full size, so you'll want to minify it.
Hey #Patrick and #Nabab none of the solutions worked for me but some how managed to work around with this script and it is working fine in even in IE 6-9 and Firefox. I didnt get chance to check on Opera, Chrome and Safari will try it later.
$(document).ready(function() {
$('ul.menu li:not(:has(li))')
.addClass(function() {
return (
($('span.separator',this).length)
? 'seplink' : (($('a[href]',this).length)
? 'anclink' : null));
});
});
This script was contributed by some generous member yesterday here as an answer to this question. I couldn't get his / her name but don't know why later it disappeared from the list of answers. Luckily I had already copied it by then. I am posting this as an answer as it is working perfectly for me.
I am anyway using the entire jQuery library because there are other aspects of my template which need it. Kindly advise if you guys see any challenge which I may not know, in using this script.
We have a variable with hmtl code inside.
<?php echo $list; ?>
This will give something like:
<li><a href='http://site.com/2010/' title='2010'>2010</a></li>
<li><a href='http://site.com/2009/' title='2009'>2009</a></li>
<li><a href='http://site.com/2008/' title='2008'>2008</a></li>
Want to add class for each <li>, it can be taken from title attribute:
<li class="y2010"><a href='http://site.com/2010/' title='2010'>2010</a></li>
<li class="y2009"><a href='http://site.com/2009/' title='2009'>2009</a></li>
<li class="y2008"><a href='http://site.com/2008/' title='2008'>2008</a></li>
We should work with variable $list.
Tentative scheme:
search for title attribute in each
<li>....</li>
throw its value to the class, which we add for opening <li>
PHP solution wanted.
Thanks.
Parsing the DOM sounds like overkill to me, if I understand the problem you're facing. Assuming that you know for sure that the entire contents of the $list variable will be structured as <li><a href='foo' title='bar'>bar</a></li> then you can do what you're asking pretty easily by combining regular expressions with a loop:
$list = "<li><a href='http://site.com/2010/' title='2010'>2010</a></li>
<li><a href='http://site.com/2009/' title='2009'>2009</a></li>
<li><a href='http://site.com/2008/' title='2008'>2008</a></li>";
preg_match_all("/title='([^']*)'/s",$list,$matches); //this gets all titles
$output=$list;
foreach($matches[1] as $match) { //this applies the titles to the li elements
$location = strpos($output,"<li>");
$output = substr($output,0,$location)."<li class='".$match."'>".substr($output,$location+4);
}
If you echo $output:
<li class="y2010"><a href='http://site.com/2010/' title='2010'>2010</a></li>
<li class="y2009"><a href='http://site.com/2009/' title='2009'>2009</a></li>
<li class="y2008"><a href='http://site.com/2008/' title='2008'>2008</a></li>
I accomplished this by splitting the text into an array, and performing a search/replace once the year is obtained.
$carrReturn="\r\n"; //Set the Newline and Return string to search for
$arr = explode($carrReturn, $list); //Break the text into an array
$list=""; //clear $list
for ($x=0; $x<count($arr); $x++){
$current=$arr[$x];
$year= strip_tags($current); //Get the year by stripping the HTML tags.
$list.=str_replace("<li", "<li class=\"y".$year."\"",$current)."\r\n";
//Reconstruct $list
}
Output
<li class="y2010"><a href='http://site.com/2010/' title='2010'>2010</a></li>
<li class="y2009"><a href='http://site.com/2009/' title='2009'>2009</a></li>
<li class="y2008"><a href='http://site.com/2008/' title='2008'>2008</a></li>
I dont know why you guys are so obsessed with Regex. DOM is clean and readable:
$dom = new DOMDocument;
$dom->loadXML("<ul>$list</ul>");
$xPath = new DOMXPath($dom);
foreach($xPath->query('//li/a/#title') as $node) {
$node->parentNode->parentNode->setAttribute('class', $node->nodeValue);
}
echo $dom->saveXML($dom->documentElement);
Outputs:
<ul>
<li class="2010">2010</li>
<li class="2009">2009</li>
<li class="2008">2008</li>
</ul>
RegEx:
preg_replace("/<li>(<a .+ title=')(\d{4})'/", "<li title='y$2'>$1$2", $string);
This really depends on every li and anchor being formatted the same exact way each time though.