7

Is there a way of creating an empty attribute for a shortcode?

Example:

function paragraph_shortcode( $atts, $content = null ) {

   return '<p class="super-p">' . do_shortcode($content) . '</p>';
}

add_shortcode('paragraph', 'paragraph_shortcode'); 

User types

[paragraph] something [/paragraph]

and it shows

<p class="super-p"> something </p>

How to add an empty attribute functionality to my first code? So when user types

[paragraph last] something [/paragraph]

It will output:

<p class="super-p last"> something </p>

I believe adding a:

extract( shortcode_atts( array(
            'last' => '',
        ), $atts ) );

is a good start, but how to check if user used the "last" attribute while it doesn't have a value?

6 Answers 6

1

There could be a couple ways to do this. Unfortunately, I don't think any will result in exactly what you're going for. ( [paragraph last] )

  1. You could just create separate shortcodes for [paragraph_first] [paragraph_last] [paragraph_foobar] that handle $content without needing any attributes

  2. You could set the default value for last to false instead of '', then require users to do [paragraph last=""]content[/paragraph]

  3. You could add a more meaningful attribute such as position which could then be used like [paragraph position="first"] or [paragraph position="last"]

Because of the fact that WordPress discards any atts not given defaults, and the fact that an att without an ="value" is given the default value same as if it's not listed at all, I don't see any way to achieve [paragraph last]

Hopefully one of my 3 workaround will prove useful. Good luck!

0
13

WordPress does not discard attributes without values as is suggested by SethMerrick, but it does not handle it as you would expect. In your example, you would not be able to check isset($atts['last']), but "last" is passed as a value to an unnamed attribute. In this case, this would result in TRUE: ($atts[0] == 'last'). So, you could do a foreach loop to check for any attribute with a value of "last".

4
  • 1
    This is how I build shortcodes for clients often. Easy to type, easy to check.
    – fuxia
    Commented Mar 23, 2012 at 23:20
  • 1
    +1 for actually answering the question. (SethMerrick's workarounds are certainly logical, but miss the functionality described in the question.) @toscho — What code do you use to do this? Any way to share? (Could be posted as a separate answer.)
    – supertrue
    Commented May 30, 2013 at 8:25
  • This answer would specifically apply to the example above, but it isn't really a good answer because an implementation of it would fail to distinguish [example last] from [example whatever="last"], since it checks values and ignores indices.
    – M Miller
    Commented Nov 12, 2013 at 18:57
  • Great solution. The selected answer doesn't actually solve the question like yours does, though it does present some valid alternatives.
    – Mike
    Commented Dec 23, 2016 at 22:01
12

When [example attr1="value1" attr2 attr3="value3" attr4] is parsed, the $atts parameter yields:

$atts = array (
    'attr1' => 'value1',
          0 => 'attr2',
    'attr3' => 'value3',
          1 => 'attr4'
);

You can normalize this like so, then you can call shortcode_atts on it.

if (!function_exists('normalize_empty_atts')) {
    function normalize_empty_atts ($atts) {
        foreach ($atts as $attribute => $value) {
            if (is_int($attribute)) {
                $atts[strtolower($value)] = true;
                unset($atts[$attribute]);
            }
        }
        return $atts;
    }
}

function paragraph_shortcode ($atts, $content = null) {
    extract(shortcode_atts(
        array (
            'last' => ''
        ),
        normalize_empty_atts($atts)
    ));

    return '<p class="super-p'
        .($last ? ' last' : '')
        .'">'
        .do_shortcode($content)
        .'</p>';
}

add_shortcode('paragraph', 'paragraph_shortcode');

Other notes:

  • [paragraph last] would make $last = true. [paragraph last="1"] would make $last = '1'.
  • Attributes are case-insensitive, so [paragraph LAST] is equivalent to [paragraph last].
  • Attributes cannot be purely numeric, so the foreach loop I created is safe against integer-indexed attributes.
2
  • 1
    +1. Such functionality should really be embedded to the core, so that WP provides attributes in a more consistent way. Well done!
    – geomagas
    Commented Mar 1, 2014 at 8:35
  • Is it necessary to lowercase the attribute? I think WP stores all attributes in lowercase by default. In addition, the code in this answer may not work with attributes that have multiple parameters, like [shortcode itemcode=1,3,4] Commented Mar 7, 2023 at 3:31
5

I know this is old, but I didn't feel any of the answers were complete, compact answers. Many people are suggesting running the $atts array through a foreach to find if your value exists. In reality, I don't feel it needs to be that complicated; array_search could be used instead to simplify things.

In the case of your example, you could use this:

[paragraph last] something [/paragraph]

then to determine if the empty attribute is provided, use a basic conditional to check and see if the key returned null or as an integer (including zero):

// Returns the array key, if empty attribute is found
$last_key = array_search('last', $atts);

// Determine if the key is an integer, zero or greater
// (In the event you're passing multiple empty attributes)
if(is_int($last_key)) {
  // Do something, your attribute is set
}
1

I found this : http://richjenks.com/wp-shortcode-attributes-without-values/

CODE

function get_flags($atts) {

    $flags = array();

    if (is_array($atts)) {
        foreach ($atts as $key => $value) {
            if ($value != '' && is_numeric($key)) {
                array_push($flags, $value);
            }
        }
    }

    return $flags;

}

USAGE

$flags = get_flags($atts);
if (in_array('your_flag', $flags) {
    // Flag is present
}
1
  • 1
    thank you for the -1 ! this solution works with less feature and less code.
    – benoît
    Commented Apr 2, 2014 at 15:47
1

The accepted answer here is both wrong and right. The phrase:

"WordPress discards any atts not given defaults"

is only true only of the shortcode_atts function and not the WP shortcode functionality as a whole.

If you look at the code below:

add_shortcode( 'paragraph', 'custom_shortcode_paragraph' );
function custom_shortcode_paragraph( $atts ) {
    // Attribute Defaults Function 
    $atts = shortcode_atts(
        array(
            'last' => false,
        ),
        $atts
    );
    $isLast = $atts['last'] !== false;
}

A shortcode usage like [paragraph last] something [/paragraph] will always have a value of false for the $isLast var.

The issue is that when the shortcode_atts function runs it does discard the attributes without values. However, they are absolutely in the $atts array before that point though. A var_dump of $atts as the first line of the custom_shortcode_paragraph function would produce:

array(1) {
  [0]=>
  string(4) "last"
}

So the 0th item in the array is a string of the attribute name forced to lowercase.

Let's change the code to the following instead:

add_shortcode( 'paragraph', 'custom_shortcode_paragraph' );
function custom_shortcode_paragraph( $atts ) {
    // look for the existance of the string 'last' in the array
    $last = in_array('last', $atts); // $last is now a boolean
    // Attribute Defaults Function 
    $atts = shortcode_atts(
        array(
            'last' => $last, // default to true if the attribute existed valueless
        ),
        $atts
    );
    $isLast = $atts['last'] !== false;
}

You now have $atts['last'] === true && $isLast === true for the shortcode:

[paragraph last] something [/paragraph]

if you do end up adding a value then the shortcode:

[paragraph last="any value at all"] something [/paragraph]

would yeild $atts['last'] === "any value at all" && $isLast === true. $last would be false because the $atts array in the beginning has the contents:

array(1) {
  ["last"]=>
  string(16) "any value at all"
}

So the value of the array item is no longer the attribute name and in_array('last', $atts) === false so the default value is shortcode_atts is false. but that is just a default that gets overwritten by the actual value of any value at all and then $atts['last'] !== false is true because "any value at all" !== false.

All said, I think this does what you want and is resilient to user error.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.